Backup & restore
etcd snapshots and an offline restore.
Lose etcd and you lose the whole cluster. The containers already running on your machines keep humming along for a while, but every record of what is supposed to exist is gone. etcd (you say it "et-cee-dee") is the cluster's key-value database, the one place Kubernetes writes down every fact about itself: which Deployments exist, every Secret, every RBAC (Role-Based Access Control) rule, every ConfigMap. It's the cluster's only logbook. A backup is a photocopy of that logbook. A restore hands Kubernetes a fresh copy and says, this is the truth now. Nodes, control-plane binaries, certificates, you can rebuild all of those from files and images you already have. What's written inside etcd you cannot rebuild from anything except a backup.
What a snapshot actually captures
Because etcd holds every object, a single snapshot is a complete point-in-time copy of the entire cluster. One file, everything in it. There's a big exception that catches people out, though. The snapshot does not include the data inside your persistent volumes. A persistent volume, or PV, is the real disk that a database Pod writes its rows onto (a Pod is the smallest thing Kubernetes runs, one or more containers sharing a single network address). etcd only stores the PV object, the little index card that says "this volume exists and mounts here." The rows themselves live on your storage backend and need their own separate backup. Restore etcd and every object definition comes back. The bytes on the disks do not.
$ sudo ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%F).db \--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
{"level":"info","ts":"2026-07-16T09:12:01Z","caller":"snapshot/v3_snapshot.go:73","msg":"fetching snapshot","endpoint":"https://127.0.0.1:2379"}{"level":"info","ts":"2026-07-16T09:12:01Z","caller":"snapshot/v3_snapshot.go:88","msg":"fetched snapshot","endpoint":"https://127.0.0.1:2379","took":"138ms"}Snapshot saved at /backup/etcd-2026-07-16.db
That stack of certificate flags isn't decoration. etcd only talks to clients that can prove they're allowed in, so you hand it a certificate and key it already trusts, the ones kubeadm generated for etcd itself, and 127.0.0.1:2379 is etcd's client port on the control-plane node. Two habits separate a real backup from a decorative one. Copy the .db file off the node the moment it's written, because a snapshot that dies with the control-plane node saved you nothing. And encrypt it, because that file holds every Secret in the cluster, and unless you turned on encryption at rest they sit inside it in a form anyone can decode with one command. Don't do this by hand once and call it done, either. Put the snapshot on a schedule, keep the last several, and ship each one somewhere the cluster can't drag down with it. Before you trust any snapshot, read it back and confirm it holds roughly the number of keys you expect. etcdutl does that offline, without touching the live cluster. (etcdutl is the newer offline companion to etcdctl. Older guides run etcdctl snapshot status and restore, which still work but are now deprecated in favor of etcdutl.)
$ sudo etcdutl snapshot status --write-out=table /backup/etcd-2026-07-16.db
+----------+----------+------------+------------+| HASH | REVISION | TOTAL KEYS | TOTAL SIZE |+----------+----------+------------+------------+| 8d3f21ab | 248117 | 1974 | 6.4 MB |+----------+----------+------------+------------+
Restoring is an offline operation
You can't rebuild an engine while it's spinning, and you can't restore etcd while etcd is live. Writing a restore over a running database corrupts it, so the procedure is deliberately offline. On a kubeadm cluster the steps are always the same. etcd and the API server (the front door every kubectl command and every controller talks to) both run as static Pods, which means the kubelet (the agent running on every node) starts them straight from manifest files in /etc/kubernetes/manifests and restarts them if they crash. Move those two manifests out of that folder and the kubelet stops the Pods. etcd is now down. Restore the snapshot into a brand-new data directory, point the etcd manifest at it, and move the manifests back. The kubelet spots them, starts etcd on the restored data, brings the API server up behind it, and the cluster wakes at the snapshot's moment. On a single control-plane node, that's the whole job. Run three stacked etcd members and you restore on every node separately, and the flags are not all the same. Each node gets its own --name and its own --initial-advertise-peer-urls, matching that member's identity, while --initial-cluster (the full list of all three members) and --initial-cluster-token have to be identical everywhere, or they refuse to form a cluster.
$ sudo mv /etc/kubernetes/manifests/{etcd,kube-apiserver}.yaml /tmp/ # stop both$ sudo etcdutl snapshot restore /backup/etcd-2026-07-16.db \--data-dir=/var/lib/etcd-restore$ sudo sed -i 's#/var/lib/etcd#/var/lib/etcd-restore#g' /tmp/etcd.yaml$ sudo mv /tmp/{etcd,kube-apiserver}.yaml /etc/kubernetes/manifests/ # restart
2026-07-16T09:20:11Z info snapshot/v3_snapshot.go:260 restoring snapshot {"path": "/backup/etcd-2026-07-16.db", "data-dir": "/var/lib/etcd-restore"}2026-07-16T09:20:11Z info membership/cluster.go:421 added member {"cluster-id": "cdf818194e3a8c32", "member-id": "8e9e05c52164694d"}2026-07-16T09:20:11Z info snapshot/v3_snapshot.go:287 restored snapshot {"path": "/backup/etcd-2026-07-16.db"}
A restore is a time machine, and it moves the whole cluster, not just the part you were trying to fix. Anything created after the snapshot is gone. Anything deleted after it comes back. So check the result: the etcd Pod should be Running, the nodes Ready, your workloads sitting where they stood when the snapshot ran. For the gaps etcd can't fill, the PV data and pulling back one deleted namespace, many teams add Velero, a tool that copies Kubernetes objects (and can snapshot volumes) to object storage and restores them one namespace at a time. That last part matters more than it sounds. An etcd restore is all-or-nothing across the cluster, so if all you lost was a single namespace someone deleted by mistake, rewinding every other change since last night just to get it back is a rotten trade. Velero lets you pull only the thing you actually lost.
$ kubectl get nodes$ kubectl get pods -n kube-system -l component=etcd
NAME STATUS ROLES AGE VERSIONcp-1 Ready control-plane 90d v1.31.4node-1 Ready <none> 90d v1.31.4node-2 Ready <none> 90d v1.31.4NAME READY STATUS RESTARTS AGEetcd-cp-1 1/1 Running 0 41s
One more thing worth knowing before you build any of this. If your cluster runs on a managed Kubernetes service (Amazon EKS, Google GKE, Azure AKS), you never touch etcd at all. The provider runs the control plane for you and won't hand you the keys, so the whole snapshot-and-restore routine simply doesn't apply. On those clusters an object backup with Velero is the main lever you've got, plus whatever volume snapshots your storage class supports. Knowing which kind of cluster you're on tells you which half of this page is even yours to run.
The step almost everyone skips is proving the restore works before they need it. A snapshot file that has never been restored is a guess, not a backup. Spin up a throwaway cluster, feed it last night's snapshot, and watch what actually comes back. The first time you run these commands should not be at 3 a.m. with production down and your pulse in your ears. Rehearse it when the stakes are zero, write down the exact order of steps, and the real event turns into a script you follow instead of a problem you solve.
Volume backups are separate from etcd. App data on PVCs needs its own story.
Test restore in a sibling environment. Untested backups are rumors.
Protect snapshot files like Secrets. They contain everything.
Try this
Take an etcd snapshot on a lab control plane, note the file size, and write down the restore steps without executing a destructive restore unless you are in a throwaway cluster.
$ sudo ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%F).db \--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$ sudo mv /etc/kubernetes/manifests/{etcd,kube-apiserver}.yaml /tmp/ # stop both$ sudo etcdutl snapshot restore /backup/etcd-2026-07-16.db \--data-dir=/var/lib/etcd-restore$ sudo sed -i 's#/var/lib/etcd#/var/lib/etcd-restore#g' /tmp/etcd.yaml$ sudo mv /tmp/{etcd,kube-apiserver}.yaml /etc/kubernetes/manifests/ # restart$ kubectl get nodes$ kubectl get pods -n kube-system -l component=etcd
Takeaway
etcd snapshots capture cluster state. Restore is offline: new data dir, repointed static pod, API back last.