Hitesh Sahu
Hitesh SahuHitesh Sahu
  1. Home
  2. ›
  3. posts
  4. ›
  5. …

  6. ›
  7. 2 2 Etcd

Loading ⏳
Fetching content, this won’t take long…


💡 Did you know?

🐙 Octopuses have three hearts and blue blood.

🍪 This website uses cookies

No personal data is stored on our servers however third party tools Google Analytics cookies to measure traffic and improve your website experience. Learn more

Loading ⏳
Fetching content, this won’t take long…


💡 Did you know?

🍯 Honey never spoils — archaeologists found 3,000-year-old jars still edible.
kubernetes

    AI-AgenticAI

    AI-DeepLearning

    AI-GenAI

    AI-Infrastructure

    AI-Machine-Learning

    AI-Math

    AWS

    Azure

    kubernetes
    • Kubernetes: Control Loops, Scheduling, and GPUs


    • Image Internals: From OCI Layers to a Running Container


    • Container Internals: What a Container Really Is


    • Kubernetes Pod Internals: What a Pod Really Is


    • Kubernetes API Server Internals


    • Kubernetes Resource Allocation: Requests, Limits, QoS, and Quotas


    • etcd Architecture Explained


    • Kubernetes Scheduler Internals


    • Kubelet Internals: The Node Agent That Runs Everything


    • Kubernetes Informers & Controllers Explained


    • Kubernetes Networking: Pods, Services, Ingress, and CNI


    • Kubernetes Storage: PV, PVC, StorageClass, and CSI


    • Helm: Kubernetes Package Manager


    • Cloud Native Observability: Prometheus, Grafana, OpenTelemetry, and Tracing


    • GPU Scheduling in Kubernetes: Device Plugins, GPU Operator & MIG


    • NVIDIA Network Operator: InfiniBand, SR-IOV, RDMA, and Multus


    • Dynamic Resource Allocation: The Future of GPU Scheduling in Kubernetes


    • Kubernetes Performance at Scale


    • Optimizing AI Inference at Scale: The Full Stack


    • Kueue: Kubernetes-Native Job Queuing and Quota Management


    • Multi-Node Distributed Training on Kubernetes


    • Kubernetes Topology Manager: NUMA-Aware GPU Scheduling


    • NVIDIA NIM: Optimized Inference Microservices on Kubernetes


    • GPU Autoscaling on Kubernetes: KEDA, HPA, and Cluster Autoscaler


    • Fine-Tuning LLMs: LoRA, QLoRA, PEFT, and NeMo on Kubernetes


    • Flash Attention: Fast, Memory-Efficient Attention for LLMs


    • Kubernetes and Cloud Native Certification Path


    • KCNA Mock Exam — Set 1


    • KCNA Mock Exam — Set 2


    • KCSA Mock Exam — Set 1


    • KCSA Mock Exam — Set 2


    • kubernetes Index


    Management

    Programming

    Terraform

    Z_Appendix

Cover Image for etcd Architecture Explained
kubernetes

etcd Architecture Explained

etcd internals for Kubernetes engineers — Raft consensus, leader election, MVCC and resourceVersion, snapshots, log compaction, the watch API, quorum loss behavior, and etcdctl operations for backup and defrag.

Kubernetes
etcd
Raft
Control Plane
Distributed Systems
Storage
← Previous

Kubernetes API Server Internals

Next →

Kubernetes Scheduler Internals

etcd Architecture Explained 🛢

etcd is a distributed, strongly consistent key-value database that stores the entire state of a Kubernetes cluster.

Every Kubernetes cluster has one component that is more important than all the others combined.

That component is etcd.

If the API Server is the brain of Kubernetes, etcd is its memory.

What Does etcd Store?

Everything Kubernetes knows. etcd is responsible for storing:

  • Cluster configuration
  • Desired state
  • Current state
  • Leader election data
  • Leases
  • Secrets
  • Events (temporary)
  • Custom Resources
    Cluster
    │
    ├── Pods
    ├── Nodes
    ├── Services
    ├── Deployments
    ├── ReplicaSets
    ├── Secrets
    ├── ConfigMaps
    ├── PVCs(PersistentVolumes)
    └── CRDs(Custom Resources)

Everything is stored as key-value pairs, eg.

    # key
    /registry/pods/default/nginx 
    
    # value
    {
      "metadata": {
          "name": "nginx"
      },
      ...
    }

Limits:

  • Default storage size limit is 2 GiB
  • Recommended max storage is 8 GiB

Why etcd?

Kubernetes needed a storage layer with three properties that are difficult to combine:

PropertyWhy Kubernetes needs it
Strong consistencyTwo controllers must never see conflicting state
High availabilityControl plane survives node failures without data loss
Watch APIControllers react to changes instantly rather than polling

etcd provides all three. A standard relational database gives you consistency but not the watch API. A cache gives you watches but not durability. etcd was purpose-built for the control-plane use case.

If etcd loses its data, Kubernetes loses its memory.

Only the API Server communicates directly with etcd.

Neither the Scheduler nor the Controller Manager talks directly to etcd. Everything goes through the API Server — which means etcd only has one client to worry about.

flowchart LR

User["User <br/>kubectl"]-->APIServer["API Server  ⚡"]

APIServer-->etcd[("etcd Cluster")]
etcd-->APIServer

APIServer-->Scheduler & Controller & Kubelet

The Watch API 👀

Controllers don't poll etcd. They subscribe to a prefix and receive events.

In practice:

  • Controllers and schedulers watch the API Server, not etcd directly.
  • The API Server maintains watches against etcd and distributes updates to other components.

Whenever a key changes, etcd immediately notifies watchers through api server.

sequenceDiagram

participant Controller
participant APIServer as API Server
participant etcd

Controller->>APIServer: WATCH /pods (startRevision=500)
APIServer->>etcd: WATCH /pods (startRevision=500)

Note over etcd: Pod nginx created (revision 501)
etcd-->>APIServer: ADDED nginx
APIServer-->>Controller: ADDED nginx

Note over etcd: Pod nginx updated (revision 502)
etcd-->>APIServer: MODIFIED nginx
APIServer-->>Controller: MODIFIED nginx

Event types:

EventMeaning
ADDEDKey created
MODIFIEDKey updated (new revision)
DELETEDKey removed

The controller reconnects with its last-seen resourceVersion if the watch stream drops.

etcd can serve events from its compacted history only up to the oldest retained revision — if a controller is too far behind it receives a 410 Gone and must re-list from scratch.


High Availability Architecture

A production etcd cluster runs an odd number of nodes — always 3, 5, or 7.

flowchart TD

APIServer["API Server ⚡"]-->Leader["Leader 🤴🏻"] 

Leader-->Follower1["Follower 👨‍💼"]
Leader-->Follower2["Follower 👨‍💼"]

Follower1<-->Follower2

Odd numbers are required because etcd uses majority voting. An even number risks a split-brain: two equally sized groups, each believing the other is down, both trying to elect a leader.

Cluster sizeNodes needed for majorityTolerated failures
321
532
743

Most production clusters use 3 nodes. 5 nodes are used when the control plane is spread across 3 availability zones and you need to tolerate a full zone failure.

flowchart LR
    Client--> API["API Server  ⚡"]

    API --> E1["etcd 1 🛢"]
    API --> E2["etcd 2 🛢"]
    API --> E3["etcd 3 🛢"]

Raft Consensus Algorithm 🚣

At any moment there is exactly one Leader. All writes go to the Leader. Followers only replicate.

etcd uses Raft to keep all nodes in sync.

Write Path

  • Leader receives write
  • Replicate to Followers
  • Majority Acknowledges
  • Commit
sequenceDiagram

participant Client
participant API as API Server
participant Leader
participant F1 as Follower 1
participant F2 as Follower 2

Client->>API: Create Pod
API->>Leader: Write entry
Leader->>F1: AppendEntries (replicate log)
Leader->>F2: AppendEntries (replicate log)
F1-->>Leader: ACK
F2-->>Leader: ACK
Leader-->>API: Commit (majority received)
API-->>Client: 201 Created

Write is committed only after a majority acknowledges it.

With 3 nodes, 2 ACKs are enough — so etcd can absorb one node failure mid-write without data loss.

Read Path

The API Server performs linearizable reads from the Leader by default

Leader sends a read with the current revision and the Leader confirms it has not been superseded.

This guarantees Kubernetes controllers always observe the latest committed state.


Quorum Loss — What Actually Happens

Cluster becomes read-only or unavailable for writes depending on the failure mode.

Losing quorum (majority of nodes) does not crash Kubernetes. It freezes write operations while keeping reads alive from the API Server's Watch Cache.

LeaderFollower1Follower 2Result
LiveLiveLiveLeader R/W, Followers W/ACK
LiveLiveDEADLeader R/W, Follower1 W/ACK
LiveDEADDEADLeader R
DEADLiveLiveReelection
flowchart TD

    TwoOfThree["2 of 3 etcd nodes fail"]
    TwoOfThree-->NoQuorum["etcd has no quorum <br/> writes blocked"]
    NoQuorum-->APIReads["API Server serves reads <br/> from Watch Cache (stale)"]

    NoQuorum-->APIWrites["API Server rejects writes <br/> 503 Service Unavailable"]
    APIReads-->ControlPlane["Controllers keep reconciling <br/> against cached state <br/> (no new state changes take effect)"]

Existing Pods keep running — kubelet is independent and does not need etcd to run containers. New Pod creates, scaling operations, and config changes are blocked until quorum is restored.

This is why etcd HA and backup are treated as cluster-critical — losing the etcd majority stops the entire control plane from accepting changes.

Leader Election

When the Leader disappears, followers hold an election:

flowchart TD

Leader["Leader — crashes"]-->Timeout["Followers detect heartbeat timeout <br/> (default: 1s election timeout)"]

Timeout-->Vote["Followers request votes"]
Vote-->NewLeader["First to get majority → new Leader"]

NewLeader-->Resume["Cluster resumes <br/> (typically <2s)"]

During the election window, writes are blocked — the API Server queues them. Reads from the Watch Cache still work because the cache is in-memory.

Raft Log

Every committed write becomes an entry in the Raft log — an append-only, ordered sequence of operations.

Index   Term   Operation
1       1      Create Namespace "default"
2       1      Create Pod "nginx"
3       2      Update Deployment "web" replicas=3
4       2      Delete Pod "nginx"
5       3      Create Service "web-svc"

Every node stores the same log. Followers that fall behind catch up by replaying entries they missed. This is how a restarted node rejoins the cluster without a full resync.


MVCC — Multi-Version Concurrency Control

etcd never overwrites data in place. Every update creates a new revision.

Revision 10:  pod/nginx  →  phase=Pending
Revision 11:  pod/nginx  →  phase=Running
Revision 12:  pod/nginx  →  phase=Succeeded

Revisions are monotonically increasing across the entire cluster — not just per object. Every write to any key advances the global revision counter.

The resourceVersion Connection

This is the same number exposed as resourceVersion in every Kubernetes object:

metadata:
  name: nginx
  resourceVersion: "12"   # ← this is the etcd revision at last write

When two controllers try to update the same object simultaneously:

  • Controller A reads revision 12, sends PUT with resourceVersion: "12" — succeeds, new revision is 13
  • Controller B also read revision 12, sends PUT with resourceVersion: "12" — etcd rejects it (revision 12 is stale)
  • Controller B receives 409 Conflict, re-fetches at revision 13, retries

This is optimistic concurrency without any locking. etcd's MVCC is what makes it safe.

MVCC and Watches

MVCC also powers the Watch API. A client can watch from any past revision:

WATCH /pods  startRevision=11

etcd replays all events since revision 11 and then streams new ones. If a controller disconnects and reconnects, it resumes the watch from its last-seen revision — no events are missed.


Snapshots and Compaction 💾

Snapshots

The Raft log grows unboundedly. Replaying millions of entries after a restart would take minutes.

etcd solves this with periodic snapshots — a point-in-time serialization of the entire key-value store.

flowchart TD

    RaftLog["Raft Log <br/> entries 1 – 5,000,000"]
    RaftLog-->Snapshot["Snapshot 💾 <br/> (state at entry 5,000,000)"]
    Snapshot-->NewLog["New log <br/> entries 5,000,001+"]

On restart, etcd loads the latest snapshot and replays only the log entries after it. Recovery time drops from minutes to seconds.

etcd triggers a snapshot automatically when the log exceeds --snapshot-count entries (default: 100,000).

Log Compaction

After a snapshot, old log entries are no longer needed. Compaction discards them and reclaims disk space.

Before compaction:  entries 1 – 5,000,000  (large)
After compaction:   entries 4,900,001 – 5,000,000  (small)

The entries before the snapshot can be safely discarded because the snapshot already captures that state.

Database Defragmentation

MVCC keeps old revisions alive until compaction removes them, but compaction leaves holes in the backend database file — it does not shrink the file on disk.

etcdctl defrag rewrites the backend file, reclaiming the holes:

etcdctl defrag --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

Run defrag on followers first, then the leader. Each node is briefly unavailable during defrag, so running one at a time prevents quorum loss.


etcdctl — Operational Commands

etcdctl is the CLI for managing etcd. Always set the v3 API:

export ETCDCTL_API=3
ETCD_FLAGS="--endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key"

Check cluster health:

etcdctl $ETCD_FLAGS endpoint health
# https://127.0.0.1:2379 is healthy: successfully committed proposal

etcdctl $ETCD_FLAGS endpoint status --write-out=table
# ENDPOINT           ID    VERSION  DB SIZE  IS LEADER  RAFT TERM  RAFT INDEX
# 127.0.0.1:2379  abc123  3.5.9    42 MB    true       5          1048576

Take a snapshot (backup):

etcdctl $ETCD_FLAGS snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db

etcdctl snapshot status /backup/etcd-snapshot-20260707.db --write-out=table
# HASH     REVISION  TOTAL KEYS  TOTAL SIZE
# a1b2c3d4  1048576   12345       42 MB

Restore from snapshot:

etcdctl snapshot restore /backup/etcd-snapshot-20260707.db \
  --name etcd-0 \
  --initial-cluster etcd-0=https://10.0.0.1:2380 \
  --initial-advertise-peer-urls https://10.0.0.1:2380 \
  --data-dir /var/lib/etcd-restore

Read a Kubernetes object directly from etcd:

etcdctl $ETCD_FLAGS get /registry/pods/default/nginx --print-value-only \
  | protoc --decode_raw   # objects are stored as protobuf, not JSON

Performance Metrics That Matter

etcd performance determines control plane latency. Key metrics exposed via Prometheus:

MetricWhat it measuresAlert threshold
etcd_disk_wal_fsync_duration_secondsTime to flush WAL to diskp99 > 10 ms
etcd_disk_backend_commit_duration_secondsTime to commit a batch to BoltDBp99 > 25 ms
etcd_server_leader_changes_seen_totalLeader elections since start> 3 in 1 hour
etcd_server_proposals_failed_totalFailed Raft proposalsAny increase
etcd_mvcc_db_total_size_in_bytesRaw DB file sizeAlert at 6 GB (hard limit 8 GB)
etcd_mvcc_db_total_size_in_use_in_bytesActual data in useDifference from above = defrag needed

The WAL fsync duration is the most sensitive indicator of disk health. SSD-backed etcd should stay under 1 ms p99. Network-attached storage (NFS, EBS gp2) regularly exceeds 10 ms — the main reason cloud providers run etcd on locally-attached NVMe.


Key Takeaways

etcd is Kubernetes' memory.

  • It stores the entire cluster state as strongly consistent key-value data.
  • It uses the Raft consensus algorithm to ensure data consistency across multiple nodes.
  • It is accessed only through the Kubernetes API Server.
  • Every Kubernetes object, from Pods to Custom Resources, is ultimately persisted in etcd.

Without etcd, Kubernetes cannot remember what the cluster should look like, making it one of the most critical components of the control plane.

ConceptPurpose
RaftLeader-based consensus — all writes replicated to majority before commit
Leader ElectionAutomatic failover in < 2 s when leader crashes
Raft LogOrdered, append-only record of every change — enables follower catch-up
MVCCEvery write creates a new revision — enables watches and optimistic concurrency
resourceVersionThe etcd revision number exposed on every Kubernetes object
SnapshotsPoint-in-time state capture — fast recovery without log replay
CompactionRemoves old MVCC revisions to reclaim storage
DefragShrinks the on-disk BoltDB file after compaction
Watch APIEvent stream from a revision — controllers subscribe rather than poll
Quorum lossWrites blocked, reads from cache still work, Pods keep running

etcd is the only stateful component in the Kubernetes control plane. Everything else can be restarted from scratch — the API Server, Scheduler, and Controller Manager are all stateless. etcd cannot. That is why backup, HA, and disk latency are treated as cluster-critical concerns.


Related Posts

  • Kubernetes API Server Internals — the only component that talks to etcd directly
Hitesh Sahu
Written by Hitesh Sahu, a passionate developer and blogger.

Tue Jul 07 2026

Share This on

← Previous

Kubernetes API Server Internals

Next →

Kubernetes Scheduler Internals

kubernetes/2-2-Etcd
Let's work together
hiteshkrsahu@gmail.com
Munich 🥨, Germany 🇩🇪, EU
Playstore
Hitesh Sahu's apps on Google Play Store
Need Help?
Let's Connect
Navigation
  Home/About
  Skills
  Work/Projects
  Lab/Experiments
  Contribution
  Awards
  Art/Sketches
  Thoughts
  Contact
Links
  Sitemap
  Legal Notice
  Privacy Policy

Made with

NextJS logo

NextJS by

hitesh Sahu

| © 2026 All rights reserved.