Lesson 07 of 19 · Real-world incident scenarios
etcd out of space at 2 a.m.
It's 2 a.m. and writes fail across the cluster with 'etcdserver: mvcc: database space exceeded'. Tell the quota alarm apart from a full disk, take a snapshot, compact, defragment one member at a time, disarm the alarm, and find what filled etcd in the first place.
The page
02:03 — KubeAPIErrorsHigh. Deployments fail with etcdserver: mvcc: database space exceeded. Existing pods keep running, but nothing new can be created, scaled or updated. The on-call engineer needs to restore writes without losing cluster state.
First five minutes
- Impact: running workloads are fine; changes fail (deploys, scaling, leader elections that write leases, and eventually controllers). Treat it as SEV2 (SEV1 if autoscaling or failover depends on writes right now).
- Freeze changes: pause CI/CD and GitOps syncs; they'll only add writes and noise.
- Confirm it's etcd, not the API server: the error text names etcd.
etcd is the school's one big register book. It has a page limit set on purpose. When the book is full, the teacher refuses new entries (writes) until old, crossed-out pages are cleared away (compaction) and the book is rebound tightly (defragmentation). Then you find out who was scribbling hundreds of notes a minute.
Quota alarm or full disk?
$ export ETCDCTL_API=3
$ ETCD="--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"
$ etcdctl $ETCD endpoint status --cluster -w table
$ etcdctl $ETCD alarm list
memberID:8e9e05c52164694d alarm:NOSPACE
$ df -h /var/lib/etcd
(Paths are kubeadm defaults; etcdctl may need installing on the host, or run it inside the etcd pod with kubectl -n kube-system exec etcd-<node> -- etcdctl ….)
| Finding | Meaning |
|---|---|
alarm:NOSPACE, disk has room |
Database hit its quota (default 2 GiB) |
| Disk at 100% | Filesystem full (logs, snapshots, images on the same disk); etcd may crash |
| Both | Fix disk space first, then the quota |
Recover (quota case)
$ etcdctl $ETCD snapshot save /var/backups/etcd-$(date +%F-%H%M).db # 1. safety first
$ rev=$(etcdctl $ETCD endpoint status -w json | jq '.[0].Status.header.revision')
$ etcdctl $ETCD compact "$rev" # 2. drop old revisions
$ etcdctl $ETCD defrag --endpoints=https://10.10.0.12:2379 # 3. one member at a time (followers first)
$ etcdctl $ETCD defrag --endpoints=https://10.10.0.13:2379
$ etcdctl $ETCD defrag --endpoints=https://10.10.0.11:2379 # leader last
$ etcdctl $ETCD endpoint status --cluster -w table # DB size should drop
$ etcdctl $ETCD alarm disarm # 4. clear the alarm
(Adjust endpoint addresses to your members. Compaction removes history, not current data; the API server normally compacts every few minutes, so a big gap means something unusual is growing.)
Full disk case: free space on the etcd volume first (old snapshots, logs, unrelated files on the same disk); never delete files inside the etcd data directory by hand.
Verify
kubectl create deploy smoke --image=registry.k8s.io/pause:3.10succeeds; delete it.- Controllers catch up (Deployments reconcile, events flow).
- Resume GitOps/CI.
Find what filled it
$ kubectl get events -A --no-headers | wc -l
$ kubectl get events -A --sort-by=.count | tail
$ etcdctl $ETCD get / --prefix --keys-only | cut -d/ -f3 | sort | uniq -c | sort -rn | head
Common causes: a controller or operator rewriting objects in a loop, event storms from a crash-looping workload, very large Secrets/ConfigMaps, Helm release history kept forever (each release is a Secret), CRDs storing big objects.
Prevent
- Alert on DB size (e.g.
etcd_mvcc_db_total_size_in_bytesabove 70–80% of the quota) and on the NOSPACE alarm. - Fix the source (loop, history limits such as Helm
--history-max, event rate). - Right-size the quota deliberately (etcd documents a practical maximum; larger databases slow recovery), and defragment periodically in maintenance windows.
- Keep etcd on its own fast disk, separate from logs and images; keep regular snapshots off-node (see Kubernetes Administration, lesson 13).
Try it: fill etcd safely (lab only)
- In a disposable kubeadm lab, set a small quota (e.g.
--quota-backend-bytes=104857600in the etcd static pod manifest). - Write a loop creating and deleting ConfigMaps with large data until the NOSPACE alarm triggers.
- Practise the full recovery: snapshot, compact, defrag each member, disarm.
- Find the "culprit" with the key-count command.
- Add a DB-size alert to your monitoring.
Going deeper: etcd health
- Watch fsync and commit latency too; slow disks cause leader elections and API slowness.
- Large clusters benefit from separating events into their own etcd (
--etcd-servers-overrides), where supported by your distribution. - Managed Kubernetes (EKS etc.) handles etcd for you, but huge objects or churn can still hit API limits.
Recap
- NOSPACE = quota reached; a full disk is different. Check both.
- Snapshot → compact → defrag one member at a time → disarm, then verify writes.
- Find the source of growth and alert on DB size before the quota.
This site is a public version of my personal engineering knowledge hub. It intentionally excludes confidential company information and internal operational details.