etcd Compaction and Defragmentation in Kubernetes
Why the database behind your cluster keeps growing even when you delete things, how compaction forgets old history, and why only defragmentation gives the disk space back.
Imagine a diary in which you are never allowed to erase anything. When a fact changes, you do not scratch out the old line. You turn to a fresh page, write the new fact, and keep the old page exactly as it was. After a year, the diary is thick with pages that nobody will ever read again, yet every one of them still takes up room on the shelf.
Kubernetes keeps its entire state in a database called etcd, and etcd behaves much like that diary. Every change to every Pod, Node, Secret and ConfigMap is written as a new version. Old versions stay behind. Left alone, the database swells until one day it hits a size limit and the whole cluster stops accepting changes. Two housekeeping jobs prevent this: compaction, which forgets old versions, and defragmentation, which hands the freed space back to the disk. They sound alike but do very different things, and mixing them up is one of the most common causes of etcd trouble.
In this guide you will learn how etcd stores history, what exactly each job does, why compaction alone never shrinks the file, how to run both safely in a live cluster, and how to watch for the warning signs. Every new word is explained the first time it appears, and nothing is assumed.
1What Are Compaction and Defragmentation?
Two jobs, one goal: keep the database small, fast and healthy.
etcd (pronounced “et-see-dee”) is a small, reliable database that stores key-value pairs. A key is a name, such as the address of a Pod, and a value is the data saved under that name. Kubernetes uses etcd as its single source of truth. If etcd were lost, the cluster would forget every Deployment, Service, Secret and Node it ever knew about.
Because etcd never overwrites data in place, it needs two clean-up operations.
Compaction
A logical clean-up. It tells etcd to forget every old version of every key older than a chosen point in time, keeping only the latest value of each key. It frees space inside the database file but does not make the file smaller.
Defragmentation
A physical clean-up. It rewrites the database file without the empty gaps that compaction left behind, and returns that room to the operating system. This is the step that actually shrinks the file on disk.
Think of a thick ring binder where every update to a form is a new sheet. Compaction is pulling out all the old, superseded sheets and throwing them away. The binder now has loose gaps between the sheets that remain, but it is still the same fat binder. Defragmentation is taking the remaining sheets, putting them into a fresh slim binder with no gaps, and returning the big one to the shelf. You need the first step to create the free space and the second to make it count.
The one-sentence difference
Compaction decides what history to forget. Defragmentation decides how much disk space to give back. Forgetting never gives anything back by itself.
2Why etcd Needs Both Jobs
etcd keeps history on purpose
Most databases overwrite a value when it changes. etcd does not. It uses a design called multi-version concurrency control, usually shortened to MVCC. Each change creates a new revision, a number that always goes up. The old value stays readable under its old revision. This is not a flaw. It is what makes two of etcd’s most important abilities possible.
- Watching. A client can say “tell me everything that changed since revision 5000”. etcd can answer only because it still remembers what happened after that point. Kubernetes controllers rely on this all day long.
- Consistent reads. A client can read the state of many keys as they were at one exact revision, so it never sees a half-updated picture.
But history is not free
If nobody ever removes old revisions, the database grows forever. A busy Kubernetes cluster writes constantly: nodes report their health every few seconds, controllers update the status of thousands of objects, and Pods start and stop all day. Each of those writes adds another revision. Even if the number of live objects stays the same, the stored history keeps piling up.
When you delete a Pod, etcd records the delete as one more revision. The earlier versions of that Pod remain in history until compaction removes them. So “I deleted lots of things, why is etcd still huge?” is a very common and very reasonable question.
The size limit makes this urgent
etcd has a built-in ceiling on the size of its database file, called the backend quota. The default is two gibibytes (GiB), and the project suggests staying well below eight. When the file reaches the quota, etcd raises an alarm and refuses every write except deletes. For a Kubernetes cluster, refusing writes is a disaster. No new Pods can be created, no status can be updated, and controllers cannot do their jobs. The cluster is effectively frozen in place, although workloads already running keep running.
Why two jobs and not one
Separating the jobs is a deliberate engineering choice. Compaction is cheap and safe to run often, because it only marks data as removable. Defragmentation is heavy: it rewrites the whole file and briefly pauses the member that is doing it. So the first is run constantly and automatically, and the second is run occasionally and with care. Understanding that difference is the heart of this topic.
3Core Concepts You Must Know First
A short vocabulary makes everything after this much easier.
Key, value and revision
A key is a name and a value is the data stored under it. In Kubernetes, the keys live under a prefix called /registry, for example a key for one Pod in one namespace. A revision is a cluster-wide counter that increases by one with every successful change anywhere in the database. Every version of every key is tagged with the revision at which it was written.
Tombstone
When a key is deleted, etcd does not erase it. It writes a small marker called a tombstone at a new revision. The tombstone says “this key stopped existing here”. Compaction later removes both the tombstone and the old versions behind it.
Backend and bbolt
etcd keeps the actual data in a single file on disk, called the backend. The storage engine that manages the file is a library named bbolt. It organises data in fixed-size blocks called pages and uses a copy-on-write approach: when something changes, it writes new pages and abandons the old ones rather than editing them. This is where fragmentation originates.
Free pages and the freelist
When compaction deletes old revisions, the pages that held them are no longer needed. bbolt records these pages in a list called the freelist and reuses them for future writes. The pages stay inside the file, so the file does not shrink. This is the central fact of the whole article.
Raft, members and leader
A production etcd runs as a group of members, usually three or five. They agree on every change using the Raft consensus protocol, which means a change counts only when a majority of members have recorded it. One member is the leader and handles the ordering of writes. Each member keeps its own copy of the database file, and so each has its own fragmentation. That is why defragmentation is done member by member.
Quota and alarm
The quota is the maximum size of the backend file. The alarm is a flag, named NOSPACE, that etcd raises when the quota is reached. While it is on, the cluster accepts reads and deletes only.
| Term | Plain-words meaning | Why it matters here |
|---|---|---|
| Revision | A number that goes up with every change | The “point in time” used to decide what to forget |
| MVCC | Keeping many versions of each key | Cause of growth and the reason compaction exists |
| Compaction | Forgetting versions older than a revision | Creates reusable free space inside the file |
| Fragmentation | Free gaps scattered inside the file | Makes the file larger than the data it holds |
| Defragmentation | Rewriting the file without the gaps | Returns space to the operating system |
| Quota | Maximum allowed file size | Hitting it stops all writes |
| Snapshot | A point-in-time backup of the database | Taken before risky maintenance |
| Watch | A stream of changes from a given revision | Affected when old revisions are compacted |
When you read about etcd size, always ask two questions: how big is the file, and how much of it is actually in use? The gap between the two is the fragmentation, and it is the number that tells you whether defragmentation will help.
4Architecture: Where etcd Sits in Kubernetes
Only one component talks to etcd directly. Seeing this makes it clear who triggers what.
In Kubernetes the kube-apiserver is the only component that reads and writes etcd. The scheduler, the controllers and the kubelets all go through the API server. The API server sends requests to etcd over a secure connection, and etcd replicates each write across its members before confirming.
flowchart TB
KUBECTL["kubectl, controllers, kubelets"] --> API["kube-apiserver replicas"]
API -->|"gRPC with TLS"| E1["etcd member 1: leader"]
API --> E2["etcd member 2"]
API --> E3["etcd member 3"]
E1 |"Raft replication"| E2
E1 |"Raft replication"| E3
E2 |"Raft replication"| E3
E1 --> W1["WAL log and bbolt database file"]
E2 --> W2["WAL log and bbolt database file"]
E3 --> W3["WAL log and bbolt database file"]
API -.->|"compact request about every 5 minutes"| E1
OPS["Operator or automation"] -.->|"defragment one member at a time"| E2
Reading the picture
The API server is the only client
Every object you create or change passes through it. It translates your request into etcd writes and reads.
The leader orders writes, all members store them
A write is accepted once a majority have it. Each member then applies it to its own database file.
Compaction is a cluster-wide, replicated action
The compact request goes through Raft like any write, so every member forgets the same revisions at the same logical moment.
Defragmentation is local to one member
It is not replicated. Each member rewrites only its own file, and it must be repeated for every member that needs it.
Two different kinds of “log”
Newcomers often confuse compaction with the clean-up of etcd’s write-ahead log. The write-ahead log, or WAL, is a sequential diary of changes used for crash recovery, and etcd trims it automatically by taking periodic snapshots. That is a separate mechanism. The compaction in this article concerns the versions of keys inside the database, not the recovery log.
On managed services such as GKE, EKS and AKS, the provider runs etcd for you and performs this maintenance. You cannot log in to etcd there. This guide matters most when you run your own control plane, for example with kubeadm, on virtual machines or on bare metal, and it also explains the symptoms you may hear about from a provider.
5How etcd Stores History: A Small Worked Picture
Follow one key through five changes and watch the revisions pile up.
Suppose the cluster starts empty and the revision counter is at 100. A Deployment’s replica count is stored in a key. Here is what happens over a few minutes.
| Revision | What happened | What etcd stores for this key |
|---|---|---|
| 101 | Deployment created with 3 replicas | Version 1: three replicas |
| 102 | Another unrelated object changed | Nothing new for this key |
| 103 | Scaled up to 5 replicas | Version 2: five replicas |
| 104 | Status updated by a controller | Version 3: five replicas, new status |
| 105 | Scaled to 4 replicas | Version 4: four replicas |
| 106 | Deployment deleted | Tombstone marker |
At revision 106 the Deployment is gone from the point of view of Kubernetes. Yet etcd still holds four versions plus a tombstone for that key. Nothing was lost, and nothing was freed.
What compaction does to this history
Say someone asks etcd to compact at revision 105. etcd then removes every version that is older than revision 105 and is not the newest version at or before it. The rule keeps the latest value as of revision 105, because that is what a reader at revision 105 would see. Older versions are dropped. The result: versions 1, 2 and 3 vanish, version 4 is kept for now, and the tombstone at 106 is newer than the compaction point so it stays. After the next compaction at a later revision, version 4 and the tombstone disappear too, and the key has fully left the database.
Once compacted, a revision cannot be read again. A client that asks for history older than the compaction point receives an error saying the required revision has been compacted. Well-written clients, including Kubernetes components, handle this by starting afresh. Chapter 12 explains how.
Why Kubernetes makes this pile up fast
Every Kubernetes update rewrites the whole object, not just the changed field. A Pod object might be a few kilobytes. Each status change stores another full copy. A cluster with ten thousand Pods, each updated a handful of times during its life, quickly generates millions of stored versions. The next chapters show how compaction removes the ones nobody needs.
Production example: the cost of frequent heartbeats
Every node in a cluster keeps a small lease object fresh as a heartbeat. In a five thousand node cluster, those heartbeats alone produce hundreds of etcd writes per second, each creating a new revision. Large operators have learned to watch these write rates closely, because heartbeats are a large share of total revision growth even though they carry almost no information.
6Compaction in Depth
Compaction is the everyday, low-risk job. Let us see who starts it, what it removes and what it leaves.
What a compaction request contains
A compaction request carries a single number: a revision. It means “forget all history older than this revision, except the newest version of each key as of that revision”. The request goes through the Raft process like any write, so every member applies it identically. Because it is replicated, you only need to send it once to any member.
Who triggers compaction in a Kubernetes cluster
The kube-apiserver
By default, the API server asks etcd to compact about every five minutes. The interval is controlled by an API server setting. When several API server replicas run, they coordinate through a small shared record so that only one of them performs each compaction. Each pass keeps roughly the most recent interval of history.
etcd’s own auto-compaction
etcd can compact on its own, in a periodic mode that keeps a time window such as one hour, or in a revision mode that keeps the last N revisions. It is off unless you enable it.
A person or script
An operator can compact manually to a chosen revision, which is the standard first step when recovering from a full database.
The cloud provider
Managed services run their own schedules and hide the details from you.
In a normal Kubernetes cluster you do not need to turn on etcd’s own auto-compaction as well. Two compactors working on the same data add no benefit. Pick one owner of this job, and the API server is already doing it by default.
How it runs without freezing the cluster
Compaction is designed to be gentle. etcd does the physical deletion in small batches with short pauses between them, so that reads and writes keep flowing while the clean-up proceeds in the background. For a database of several gibibytes, a compaction pass often completes in seconds, though a very large backlog can take longer and slightly raise latency while it runs. This is why compaction can safely happen every few minutes, while defragmentation cannot.
What compaction removes and what it keeps
Removed by compaction
- Every old version of a key that has a newer version before the compaction point.
- Tombstones and the history of keys that were deleted before the compaction point.
- The ability to read or watch from revisions older than the compaction point.
Not changed by compaction
- The current value of every live key.
- The size of the database file on disk.
- History newer than the compaction point.
Running a compaction by hand
Sometimes you want to compact yourself, for example during recovery from a full database. The procedure is simple in idea. First, ask any member for its current revision, a number shown in the status output of the command line client. Second, send a compaction request using that number. Because the request is replicated, one request to one healthy member is enough for all of them. Third, check the status again: the in-use size should drop sharply while the file size stays the same. That last observation is a useful lesson in itself, because it proves with your own eyes that compaction alone does not shrink the file.
Compacting to the very latest revision removes all history, which can make live watches fail and cause a wave of relists. In a healthy cluster prefer a revision from a few minutes ago. In an emergency with a full database, the latest revision is acceptable because getting writes back matters more than preserving a short window of history.
How much history should be kept?
Keeping very little history keeps the database small, but if a controller restarts and needs to catch up on changes it missed a few minutes ago, there must still be enough history for it to do so. Kubernetes picks a short window because its components keep their own in-memory copies of state, as chapter 12 explains. A window of a few minutes is the default, and very long windows are rarely needed. Longer windows cost disk space in direct proportion to how many writes you make.
7Why Compaction Alone Does Not Shrink the File
This is the idea people struggle with most, so we will take it slowly with pictures.
The page picture
The database file is divided into equal pages, like the pages of a notebook. As revisions are written they fill pages. Compaction removes old data, which empties some pages, but those pages are scattered all over the file between pages that are still in use. bbolt cannot hand back a page from the middle of a file to the operating system. A file is a single continuous object. You can only cut the end off a file, and the end is full of live data.
flowchart LR
subgraph B["Before compaction: 10 pages, file is 10 pages long"]
P1["live"] --- P2["old"] --- P3["old"] --- P4["live"] --- P5["old"] --- P6["live"] --- P7["old"] --- P8["old"] --- P9["old"] --- P10["live"]
end
subgraph C["After compaction: still 10 pages long"]
Q1["live"] --- Q2["free"] --- Q3["free"] --- Q4["live"] --- Q5["free"] --- Q6["live"] --- Q7["free"] --- Q8["free"] --- Q9["free"] --- Q10["live"]
end
subgraph D["After defragmentation: 4 pages long"]
R1["live"] --- R2["live"] --- R3["live"] --- R4["live"]
end
B --> C --> D
What the free pages are good for
The free pages are not wasted. bbolt remembers them in its freelist and uses them for new writes before it grows the file. In a steady cluster that compacts regularly, the file therefore settles at a size that is a little above the live data plus the recent history, and then stops growing. The file size reflects the high-water mark, the largest it ever needed to be, not the amount of live data today.
The life of a page
stateDiagram-v2
[*] --> LiveData : write creates a new revision
LiveData --> OldRevision : a newer revision of the key is written
OldRevision --> FreeInFile : compaction removes it
FreeInFile --> LiveData : reused by a later write
FreeInFile --> ReleasedToDisk : defragmentation rewrites the file
ReleasedToDisk --> [*]
So why ever defragment?
If free pages get reused, why not leave the file alone? There are three reasons.
- The quota counts the file, not the live data. A file that grew to its high-water mark during a bad hour still counts that size against the quota forever, even when most pages are free. The cluster can hit the quota with mostly empty pages.
- The file is memory-mapped. etcd maps the database file into memory for fast reads. A bloated file wastes address space and, when it exceeds the available memory, forces the operating system to read from disk more often, which slows reads.
- Backups and restores get bigger. A snapshot copies the file as it is, so a bloated file makes every backup, copy and recovery slower and larger.
A suitcase that you packed for a long trip stays the same size when you take clothes out at home. The empty space inside is available for your next trip, but the suitcase still takes up the same shelf in the cupboard and still counts against your airline’s baggage limit. Only if you swap it for a smaller suitcase do you get the shelf back.
8Defragmentation in Depth
Defragmentation is the heavy job. It is safe when done with care, and painful when done carelessly.
What happens during a defragmentation
sequenceDiagram
autonumber
participant OP as Operator or tool
participant M as etcd member
participant OLD as Old database file
participant NEW as New temporary file
participant OS as Operating system
OP->>M: Request defragmentation of this member only
M->>M: Pause serving reads and writes
M->>NEW: Create a new empty database file
M->>OLD: Read every live key and value
M->>NEW: Write them out in compact order
M->>NEW: Flush to disk
M->>OS: Replace the old file with the new one
OS-->>M: Old space returned to the filesystem
M->>M: Resume serving requests
M-->>OP: Done
Reading the steps
The member stops serving requests
While it rewrites its file, that one member cannot answer reads or writes. Other members keep working, so a healthy cluster stays available if the defragmentation is done one member at a time.
A new file is created beside the old one
This means you need spare disk room during the operation, in the worst case about the size of the live data. Running out of disk space in the middle is a real risk on a nearly full volume.
Only live data is copied across
Free pages are simply not copied, which is what removes the gaps.
The new file replaces the old one
The swap is done in a way that is safe against a crash. If the machine fails halfway, the old file is still intact.
The member resumes and catches up
If it fell behind during the pause, it quickly receives the missing changes from the leader.
How long does it take?
The time depends on the amount of live data and the speed of the disk. On fast solid-state storage it is typically a few seconds for databases of around a gibibyte, and it can grow to a minute or more for large ones. Slow or shared disks stretch the pause much longer. Measure it in a test cluster before running it in production.
Practical tips that save trouble
Measure first
Record the file size and in-use size on each member before and after. If the numbers barely move, you did not need to do it.
Mind the client timeout
A long defragmentation may exceed the default command timeout of the client. Use a generous timeout so the command does not give up while the member is still working.
Let things settle
Wait a short while between members. This lets the resumed member catch up and lets clients finish any relists before the next pause.
Avoid busy moments
Do not defragment during a large rollout, a node pool upgrade or a known traffic peak, when the control plane is already under extra load.
The leader needs special care
The leader sends regular heartbeats to the followers to prove it is alive. If the leader is paused for defragmentation for longer than the election timeout, the followers may think it has died and start an election. A new leader will be chosen quickly and the cluster will recover, but writes will stall briefly in the meantime. The safe habit is to defragment followers first and the leader last. Some teams move leadership to another member before defragmenting the original leader.
Do not defragment all members at the same moment. Each one stops serving while it works, and if a majority is paused together, the cluster loses quorum and cannot accept writes at all until some of them return. Always one at a time, and wait until the member is healthy before moving to the next.
9Compaction vs Defragmentation: Side-by-Side
Place the two jobs next to each other and the differences are easy to remember.
| Question | Compaction | Defragmentation |
|---|---|---|
| What does it change? | Which revisions exist in the database | The physical layout and size of the file |
| Nature | Logical, about data | Physical, about storage |
| Frees file space? | Inside the file only | Yes, returns it to the filesystem |
| Changes data visible to clients? | Removes the ability to read old revisions | No, all live data stays exactly the same |
| Replicated across members? | Yes, one request affects all members | No, run separately on each member |
| Blocks the member? | No, runs in small background batches | Yes, the member pauses while it runs |
| Typical frequency | Every few minutes, automatic | Occasional, when fragmentation is high |
| Needs extra disk space? | No | Yes, room for a temporary new file |
| Risk level | Low | Moderate, needs planning |
| Who normally runs it | The API server or etcd itself | An operator, a scheduled job or a tool |
| Skipping it leads to | Endless growth of history | A bloated file that can hit the quota |
Why keeping them separate is good
- The frequent job is cheap and non-blocking.
- The costly job can be scheduled in quiet hours.
- Each member can be maintained without taking the cluster down.
- Operators can decide history length and disk policy independently.
What it costs
- People must remember to do both.
- The second job is easy to forget until an alarm fires.
- Misunderstanding leads to the belief that compaction alone is enough.
- Defragmentation needs care around leaders and disk room.
10The Quota and the NOSPACE Alarm
What actually happens on the bad day, and how to get out of it.
What the quota does
The quota is a safety brake. etcd is not designed for very large databases, and a runaway database could exhaust the disk and corrupt everything. So when the file grows to the quota, etcd raises the NOSPACE alarm and rejects all writes with an error message that says the database space is exceeded. Deletes still work, because deleting is how you recover.
What the cluster looks like when the alarm is on
Writes fail
Creating, updating or scaling anything returns an error mentioning that the database space is exceeded.
Controllers stall
Status updates, leases and events cannot be written. Lease renewals fail, and leader election for controllers can start to flap.
Running Pods keep running
Containers already started are not stopped. But nothing can be changed, healed or replaced.
Reads still work
You can still inspect the cluster, which helps with diagnosis.
The recovery path
flowchart TD
A["Writes fail: database space exceeded"] --> B["Confirm the NOSPACE alarm is active"]
B --> C["Find the current revision"]
C --> D["Compact up to that revision"]
D --> E["Defragment each member, one at a time"]
E --> F["Disarm the alarm"]
F --> G["Verify that writes succeed again"]
G --> H["Find the root cause of the growth"]
H --> I["Raise the quota only if the need is real"]
Why Kubernetes feels so broken when this happens
The NOSPACE alarm is dramatic because so much of Kubernetes’ normal operation is writing. Node heartbeats stop being recorded, so after a while the control plane may start marking nodes as unreachable. Controllers that use leader election cannot renew their leases, so they may give up leadership and stop working. The scheduler cannot record where it placed Pods. The kubelets cannot report status. The API server itself keeps answering reads, which is a trap for the unwary, because a quick look at the cluster can seem fine. Only attempts to change anything reveal the problem.
This is also why the alarm is better treated as a rehearsed emergency than as a surprise. Teams that have written the recovery steps in their runbook, and have tried them once in a test cluster, usually restore writes in minutes. Teams that discover the steps during the outage often spend hours.
A test worth running
In a disposable test cluster, set a very small quota, generate writes until the alarm trips, and then follow the recovery path from this chapter. Doing it once turns an abstract diagram into muscle memory, and teaches you exactly what the error messages look like.
Why every step matters
- Compact first so that old history becomes free pages. Without this, defragmentation has nothing to reclaim.
- Defragment second so that the file really shrinks below the quota. If you skip this, the file is still at the quota and the alarm will return immediately.
- Disarm third. The alarm does not clear itself, even when space is available. Until you disarm it, etcd keeps refusing writes.
- Investigate last. Something wrote far more than usual. Finding it is what stops a repeat.
Take a snapshot backup before you start recovery if etcd will accept the command. A backup costs a few minutes and removes the fear from every step that follows.
11Data Flow and Lifecycle: From a Write to Reclaimed Space
Follow one update from the moment you press Enter until the disk space it used is finally given back.
You change an object
For example you scale a Deployment. The API server validates the change and sends the complete new object to etcd.
The leader orders it and the majority records it
The change is appended to the write-ahead log on a majority of members. Only then is it confirmed.
A new revision is created in the database
Each member applies the change to its bbolt file. The old version of the key is kept and the new one is stored under the next revision number. The file may grow.
Watchers are notified
Controllers watching that key receive the change right away. This is the main reason history exists.
Time passes and the old version becomes unnecessary
Nobody can still be watching from before that revision. It is now just weight.
Compaction removes the old version
Its pages become free inside the file. Future writes reuse them. The file size does not change.
Defragmentation rewrites the file if the gaps grow too large
The file shrinks to roughly the size of the live data and the operating system gets the space back.
Where the time goes
| Stage | Typical timescale | Who controls it |
|---|---|---|
| Write confirmed | Milliseconds | Disk speed and network between members |
| Old version becomes unneeded | Seconds to minutes | How long watchers might still need it |
| Compaction removes it | Up to the next compaction pass, minutes by default | API server compaction interval |
| Space returned to the operating system | Hours to weeks, until the next defragmentation | Your maintenance schedule |
This is why file size alone is a misleading health signal. A file that is large but has mostly free pages is healthy for writes. A file that is large and nearly full of live data is a real capacity problem. You need both numbers to tell the difference.
12Impact on Kubernetes: Watches, Revisions and “Too Old” Errors
Compaction is invisible most of the time. Here is where it can touch the rest of the cluster.
Resource versions are revisions in disguise
Every Kubernetes object carries a field called resourceVersion. It is derived from the etcd revision at which the object last changed. When a controller says “watch Pods starting from this resource version”, the API server turns that into “watch from this etcd revision”.
The watch cache shields most clients
To avoid hitting etcd for every request, the API server keeps a watch cache, an in-memory copy of recent changes for each resource type. Almost all controller and kubelet watches are served from this cache and never reach etcd directly. The cache keeps a window of recent events, so a client that disconnects briefly can resume without any help from etcd history.
When a client falls too far behind
If a client asks to resume from a resource version that is older than both the watch cache window and the compaction point, the API server cannot supply the missing changes. It answers with an HTTP 410 response meaning the resource version is too old. Well-behaved clients, such as the informers used by controllers, react by doing a fresh full list of the resource and then starting a new watch from there. This is called a relist. It is a normal, designed-for event and not a bug.
sequenceDiagram
autonumber
participant C as Controller informer
participant A as kube-apiserver
participant E as etcd
C->>A: Watch from resource version 5000
A->>E: Watch from revision 5000
E-->>A: Error: revision has been compacted
A-->>C: 410 Gone: resource version too old
C->>A: List all objects of this type
A->>E: Read current state
E-->>A: Current objects and latest revision
A-->>C: Full list with new resource version
C->>A: Start a new watch from the new version
When this becomes a problem
- Many clients relist at once. After a long etcd pause, such as a slow defragmentation or a leader election, hundreds of controllers and thousands of kubelets may fall behind together and relist simultaneously. The resulting burst of large list calls can load the API server heavily, a pattern sometimes called a thundering herd.
- Very aggressive compaction. Keeping almost no history leaves little room for slow clients to catch up.
- Overloaded or slow components. A controller that is starved of CPU may fall behind the window repeatedly and relist over and over.
Why Kubernetes can compact so often
Because clients are built to recover from “too old” by relisting, the cluster can safely compact on a short timer. History in etcd is a convenience for catching up, not the only memory in the system. The API server’s cache and each client’s own in-memory copy do most of the work.
Custom tools and scripts that hold a watch open for a long time and do not handle the “too old” response will break after the next compaction. If you write clients, always handle this case by relisting and rewatching.
13What Makes the Database Grow in Real Clusters
Finding the biggest source of writes is usually more valuable than any amount of tuning.
Events
Every Pod start, failure, image pull and scheduling decision creates an Event object. Busy clusters generate vast numbers of them. They expire after about an hour by default, but they still cause writes and deletes at a high rate.
Node and controller leases
Small heartbeat objects renewed every few seconds by every node and by leader-elected controllers. Tiny objects, but extremely frequent.
Status churn
Controllers that rewrite status fields on every reconcile loop, even when nothing meaningful has changed, create endless revisions.
Endpoint and EndpointSlice updates
Every Pod becoming ready or not ready updates the matching endpoint objects. Large Services with frequent Pod changes can be heavy writers.
Large objects
Big ConfigMaps, Secrets and custom resources store a full copy on every update. A one-megabyte object updated every minute adds over a gigabyte of history per day before compaction.
Package manager release records
Tools such as Helm store each release revision as a Secret. Many releases with large charts and frequent upgrades keep many sizeable objects alive in etcd.
How to find the biggest writers
Count objects by type under the Kubernetes prefix, and look at the API server’s request metrics broken down by resource and verb. The resource types at the top of the write counts are your suspects. Another useful approach is to list the largest keys. A handful of runaway objects is a very common cause of surprising growth.
Production example: a controller that never stops updating
A team once found their etcd file growing by hundreds of megabytes a day in a quiet cluster. The cause was a custom controller that wrote a “last checked” timestamp into the status of thousands of objects every few seconds. Nothing important changed, but every write produced a full new copy of each object. Removing the unnecessary timestamp updates cut the growth rate dramatically, and no tuning of compaction or defragmentation could have matched that fix.
A quick habit: ask “what changed?”
When growth suddenly speeds up, the most useful question is not “how do I clean this up?” but “what changed recently?”. Think through the likely suspects in order. Was a new controller or operator installed? Did a deployment tool start recording more history? Did a workload begin crash-looping and flooding Events? Was a very large ConfigMap or custom resource added and updated often? Did the cluster grow in node count, multiplying heartbeats? Teams that make a habit of asking this question, and of keeping a short log of significant changes to the cluster, find the answer in minutes instead of hours.
The API server’s own request metrics, broken down by resource and verb, usually reveal the culprit within a minute. Look for a resource type whose write rate does not match the number of objects you expect to be changing.
Separating the noisy tenants
Kubernetes allows certain resource types to be stored in a different etcd cluster from the rest. The official documentation for large clusters recommends storing Events in a separate, dedicated etcd instance, using an API server setting that overrides the etcd servers for a given resource. The very high write rate of Events then cannot slow down or bloat the main store that holds the objects your cluster truly depends on. Several large operators have described using this approach to keep their primary etcd healthy at scale.
14Planning and Sizing: Quota, Memory and Disk
Choose the quota on purpose
The default quota of two gibibytes is comfortable for small clusters and tight for large ones. Many production clusters raise it, with eight gibibytes being the figure the etcd project suggests as a practical upper limit. Beyond that, performance and recovery times degrade, and it is a sign that the cluster should be split or its noisy resource types separated. Set the quota high enough that normal growth plus a bad day does not hit it, and low enough that a runaway process is stopped before it harms the disk.
The memory-mapped file
etcd maps its database file into memory. For best read performance, the machine should have enough free memory to hold the whole file, in addition to what etcd itself needs. If the file is larger than the available memory, the operating system must fetch pages from disk during reads, and latency rises. This is a practical reason to keep the file compact and an argument for regular defragmentation even when the quota is far away.
Disk matters more than CPU
etcd waits for the disk on every write, because it must flush the write-ahead log before confirming. Slow disks cause slow writes, missed heartbeats and leader changes. Use dedicated solid-state storage with low and steady latency, and avoid sharing the disk with other busy workloads. Leave generous free space: the data file, the logs and the temporary file created during defragmentation all live on the volume.
| Planning question | Rule of thumb | Reason |
|---|---|---|
| How big should the quota be? | Several times the live data size, within the suggested upper limit | Room for history and for fragmentation between maintenance runs |
| How much disk space should be free? | At least the size of the database file, plus space for logs and snapshots | Defragmentation writes a temporary new file |
| How much memory? | Enough for etcd plus the whole database file | Keeps reads fast through the memory mapping |
| How many members? | Three for most clusters, five for higher resilience | More members add safety but also replication cost |
| Where should Events go? | A separate etcd for large, busy clusters | Isolates the heaviest write load |
Treat the quota like a fuse box. You want it to trip before the wiring melts, but not so often that you cannot run the kettle. Record the reasoning behind the number you choose so that the next engineer does not raise it blindly during an incident.
15Operating It: A Safe Defragmentation Runbook
A repeatable routine turns a frightening operation into a boring one.
When to defragment
Do not defragment on a fixed calendar alone. Base the decision on two numbers: the size of the file and the amount of it that is actually in use. A common rule of thumb is to act when the file is large in absolute terms, for example above a few hundred megabytes, and a large share of it, such as half or more, is free. The standard monitoring rules published with etcd raise a warning on very similar conditions, which makes them a good starting point.
The routine, step by step
Check cluster health
All members should be healthy, with a leader, and no active alarm. Never start maintenance on a cluster that is already struggling.
Take a snapshot backup
Save it somewhere off the machine. This is your safety net for every later step.
Compare file size and in-use size on each member
Record the numbers before you start so that you can show the effect afterwards.
Check free disk space
Confirm the volume has room for a temporary copy of the live data.
Defragment one follower
Talk only to that member’s own address, not to a load balancer in front of the cluster. Wait for it to finish.
Verify before moving on
Confirm the member rejoined, is in sync, and the file is smaller. Pause for a short while so the cluster settles.
Repeat for the other followers, then the leader last
Optionally move leadership first so the final step affects a follower.
Record the results
Note sizes, durations and any alarms. These notes make the next run faster and safer.
Tools and automation
The etcdctl command line
The official client. It supports compaction, defragmentation, status checks, alarms and snapshots, and is the basis for every other approach.
etcd-defrag
A purpose-built tool maintained under the etcd project. It can check each member’s fragmentation, skip members that do not need it, and process them one at a time with the leader last.
Scheduled jobs
A Kubernetes CronJob or an operating system timer that runs the routine in a quiet window, with alerts on failure.
Platform operators
Some distributions automate this for you. For example, OpenShift’s etcd operator defragments members automatically when fragmentation passes a threshold.
Pointing the defragmentation command at a load balancer or at all endpoints at once. It may hit several members together or the wrong member. Always target one specific member address at a time, unless you are using a tool that is built to sequence them safely.
16High Availability, Reliability and Failure Scenarios
Quorum is the number to protect
A three-member cluster needs two members to agree on any write. While you defragment one member, two remain, which is exactly enough. If another member fails or is slow at the same time, the cluster loses its majority and stops accepting writes. For this reason a three-member cluster has no room for a second problem during maintenance. Five members tolerate one planned pause plus one unexpected failure, which is one reason larger or more critical clusters use five.
What can go wrong, and what to do
| Scenario | What you see | Recovery |
|---|---|---|
| Defragmentation takes much longer than expected | Timeouts from the API server, slow kubectl, brief errors | Wait if the member is healthy. Schedule future runs in quiet windows and test durations first. |
| Leader defragmented and election occurs | A short stall in writes, a leader change event | The cluster recovers on its own. Defragment followers first next time. |
| Disk fills during defragmentation | The operation fails, possibly with a leftover temporary file | Free space, remove the temporary file after confirming the member state, and retry. |
| NOSPACE alarm raised | All writes rejected | Compact, defragment all members, disarm the alarm. |
| Member crashes during defragmentation | Member restarts with the old file intact | Check health, let it catch up, then retry. |
| Mass relist after a long pause | API server CPU and memory spike, slow responses | Wait for the herd to settle. Avoid back-to-back pauses. |
| Members differ widely in size | One member close to quota while others are fine | Defragment the large one. Differences are normal because each file is independent. |
Backups and restores
A snapshot backup is a copy of the database at one moment. It is also a handy way to understand fragmentation, because restoring a snapshot creates a fresh file that contains only live data, in effect a defragmented database. Test restoring from your backups regularly, in a separate environment, so that you know it works and how long it takes. A backup that has never been restored is only a hope.
The three members of a healthy cluster usually have slightly different file sizes. This is normal because the physical layout depends on each member’s own history of writes and restarts. Do not treat a small difference as an error.
17Performance and Scalability Effects
How a healthy or neglected database changes the speed of everything built on it.
How fragmentation hurts
A fragmented file is slower in several quiet ways. It spreads live data over more pages, so reads touch more of the disk and more of memory. It enlarges snapshots, which slows the process of bringing a new or recovering member up to date, because the leader has to send a bigger file. And it pushes the file towards the memory limit of the machine, after which the operating system starts to swap pages in and out.
How the maintenance jobs themselves cost performance
Compaction
Adds light disk activity while it runs. Usually invisible, but a very large backlog after a long gap can raise latency for a short time.
Defragmentation
Blocks one member for the duration. Overall cluster latency can rise a little because requests must use the other members only.
Relist storms
If the pause is long, clients reconnect and relist together, adding load on the API server.
Cache warm-up
After the new file is in place, the operating system must load it into memory again, so the first reads may be slower.
Reading the effect in your own graphs
After a defragmentation, three graphs should tell a clear story. The file size line drops sharply to close to the in-use line. The request latency line shows a small bump at the moment the member paused and then returns to its usual level. The leader change counter stays flat unless you defragmented the leader. If instead the latency stays high, or leader changes keep happening, something else is wrong, most likely a slow disk or a network problem between members, and the maintenance simply made it visible.
Scaling guidance
- Keep compaction frequent and automatic so that each pass is small and quick.
- Defragment only when the numbers say it will help, and run it in a quiet window.
- Reduce the cause of growth first, since every avoided write is cheaper than any clean-up.
- Split noisy resource types, especially Events, into their own etcd when the main one becomes crowded.
- At very large scale, consider several smaller clusters rather than one enormous one with a huge database.
A fair expectation
A well-run cluster with sensible write patterns reaches a steady state. The file size rises to a plateau, compaction keeps in-use data small, and defragmentation is needed only rarely, perhaps after unusual events such as a mass deployment, a bug in a controller or an event storm. If you must defragment every few days, treat it as a sign of excessive write activity somewhere and look for the cause.
18Security Considerations
etcd holds the keys to the kingdom, so every maintenance action must respect that.
Why access to etcd is access to everything
Everything in the cluster, including every Secret, lives in etcd. Anyone who can read etcd directly can read all of it and bypass the API server’s login and permission checks entirely. Anyone who can write to it can change the cluster without leaving a trace in the API audit logs. For this reason etcd must be reachable only from the control plane, with mutual certificate authentication between the API server and etcd, and between etcd members.
Security points specific to maintenance
Authenticated maintenance
Compaction and defragmentation need a valid client certificate. When etcd’s own role system is enabled, maintenance operations typically require a highly privileged role. Keep those credentials tightly controlled.
Snapshot protection
A backup contains the entire cluster state, including all Secrets. Store backups encrypted, restrict who can read them, and set a retention period.
Secrets at rest
By default Secrets are stored without encryption in etcd. The API server can encrypt them before storage through an encryption configuration. This protects the file, the backups and the snapshots.
Old data lingers
Compaction and defragmentation remove old versions logically and physically, but they do not securely wipe the underlying disk blocks. Use disk-level encryption so that discarded blocks are not readable later.
Least privilege for automation
A scheduled defragmentation job needs a client certificate that can reach etcd. Give that job its own certificate, narrow in purpose and short in lifetime where possible. Run it from a control plane node or a locked-down pod, never from a developer laptop. Log every run, so that any maintenance action on the cluster’s most sensitive data store is traceable.
Do not open the etcd client port to a wider network “just so the maintenance script can reach it”. The port gives full control over the cluster. Run maintenance from where etcd is already reachable.
19Monitoring, Logging, Metrics and Troubleshooting
The signals that matter
etcd exposes metrics in a format that monitoring systems such as Prometheus can collect. The exact metric names can differ a little between versions, but the ideas are stable.
| Signal | What it tells you | Typical alert idea |
|---|---|---|
| Database file size | How big the file is on disk | Warn when it passes a large share of the quota |
| Database size in use | How much of the file holds live data and recent history | Compare with the file size to compute fragmentation |
| Quota size | The configured limit | Use as the denominator for usage percentage |
| Fragmentation ratio | Share of the file that is free pages | Warn when the file is large and more than about half is free |
| Backend commit duration | How long it takes to commit changes to the database file | Warn when high percentiles rise |
| WAL sync duration | How long the disk takes to flush the write-ahead log | Warn when the high percentile goes above about ten milliseconds |
| Leader changes | How often leadership moved | Warn on repeated changes in a short period |
| Has leader | Whether a member can see a leader | Page when it is false |
| Compaction duration | How long each compaction took | Watch for steady growth |
| API server request latency by resource | User-visible effect of etcd slowness | Warn on sustained rises |
The monitoring rules that ship with the etcd project already contain alerts for low quota space, high fragmentation ratio, slow disk syncs and frequent leader changes. Adopt them first and tune them later, rather than writing everything from scratch.
A calm path from symptom to cause
flowchart TD
A["Symptom: slow API or failing writes"] --> B{"Do writes fail with space exceeded?"}
B -->|"Yes"| C["Follow the NOSPACE recovery path"]
B -->|"No"| D{"Is the file far larger than the in-use size?"}
D -->|"Yes"| E["Plan defragmentation, one member at a time"]
D -->|"No"| F{"Is the in-use size itself growing fast?"}
F -->|"Yes"| G["Find the heaviest writers: events, leases, status churn, large objects"]
F -->|"No"| H{"Are disk sync or commit times high?"}
H -->|"Yes"| I["Fix the slow or shared disk"]
H -->|"No"| J["Check leader changes, network and API server load"]
Reading common messages
“database space exceeded”
The quota has been reached and the NOSPACE alarm is on. Follow the recovery path in chapter 10. Remember to disarm the alarm at the end.
“required revision has been compacted”
A client asked for history that no longer exists. Usually harmless: the client should relist. If it repeats constantly for one component, investigate why that component keeps falling behind.
“too old resource version” from the API server
The same situation as seen from the Kubernetes side, with an HTTP 410 response. Look for custom clients that do not handle it.
“request timed out” from etcd
The member could not respond in time. Check for a defragmentation in progress, slow disk, heavy compaction or a network problem between members.
Logging advice
etcd logs compaction and defragmentation events with timings. Keep these logs, because they are the best record of how long maintenance really took and of whether a slow period matches a maintenance window. Send them to a central place that survives the loss of the machine itself.
20Design Patterns and Anti-patterns
Patterns that work
One owner for compaction
Let the API server do it, or etcd, but not both. Clarity avoids surprises.
Threshold-driven defragmentation
Act on measured fragmentation and size, not only on the calendar.
Followers first, leader last
Keep the member that coordinates writes available for as long as possible.
Back up, act, verify, record
Every maintenance run follows the same four steps.
Isolate noisy data
Put Events, and any other heavy writer, in a separate etcd.
Fix the writers
Treat high growth as a bug in some component and repair it at the source.
Anti-patterns to avoid
What it looks like
Running compaction and believing the file size will fall.
Why it hurts
The file does not shrink, the quota still fills, and the team is confused when the alarm appears.
Better approach
Pair compaction with defragmentation and monitor both file size and in-use size.
What it looks like
Defragmenting every member at once to “finish quickly”.
Why it hurts
A majority of members is paused together, quorum is lost and the whole cluster stops accepting writes.
Better approach
One member at a time, with a health check between each.
What it looks like
Raising the quota every time the alarm appears.
Why it hurts
The underlying cause stays, the file grows larger and slower, and recovery and backup times balloon.
Better approach
Find the heavy writers and reduce them. Raise the quota only for a proven need and within the supported range.
What it looks like
Forgetting to disarm the alarm after recovery.
Why it hurts
etcd keeps refusing writes even though space is available, and the outage drags on.
Better approach
Make “disarm and verify a write” an explicit final step of the runbook.
21Best Practices and Common Mistakes
Best practices
Know who compacts
Check the API server setting for the compaction interval and confirm that etcd’s own auto-compaction is either off or deliberately chosen.
Alert before the fuse blows
Warn at a comfortable share of the quota, so you have hours or days, not minutes, to react.
Automate defragmentation with safeguards
Use a tool or job that checks health first, works one member at a time, does the leader last and stops on any error.
Keep dedicated fast disks
Storage quality prevents more trouble than any tuning flag.
Back up regularly and test restores
Maintenance is calm only when recovery is proven.
Review the biggest writers every quarter
New controllers and new tools change the write pattern. Catch bad ones early.
Document the runbook
At two in the morning, nobody wants to remember the order of the steps.
Common mistakes
- Treating compaction and defragmentation as the same thing. They are not. One forgets, the other reclaims.
- Judging health by file size only. Compare it with in-use size.
- Running defragmentation through a load balancer. It may hit the wrong member or several at once.
- Starting without enough free disk. The temporary file needs room.
- Ignoring slow disks. Many “etcd problems” are disk problems.
- Letting Events share the main etcd in a very busy cluster. They are the loudest writer in most clusters.
- Skipping the backup because the operation is “routine”. Routine operations are where rare failures hurt most.
22A Worked Example With Real Numbers
Let us walk through a realistic story from calm, to trouble, to repair. The cluster here is an illustration, not a specific company.
The cluster
A three-member etcd cluster serves a mid-sized Kubernetes cluster. The quota is set to 8 GiB. The API server compacts every five minutes. Nobody has defragmented for a year.
| Moment | File size | In use | Free pages | Fragmentation |
|---|---|---|---|---|
| Calm, a year ago | 1.6 GiB | 1.2 GiB | 0.4 GiB | 25 percent |
| After a night of runaway Events | 6.2 GiB | 5.9 GiB | 0.3 GiB | 5 percent |
| Next morning, after compaction | 6.2 GiB | 1.4 GiB | 4.8 GiB | 77 percent |
| After defragmenting one member | 1.5 GiB | 1.4 GiB | 0.1 GiB | 7 percent |
What the numbers mean
The storm
A misbehaving workload restarted thousands of times overnight, producing a flood of Events. The live data briefly reached 5.9 GiB. The file grew to hold it.
The false comfort
Once the Events expired and compaction ran, the in-use data fell back to 1.4 GiB. The monitoring dashboard that only showed “in use” looked healthy. The file, however, remained at 6.2 GiB.
The danger
The file sat at 78 percent of the 8 GiB quota. A second, smaller storm of just 2 GiB of new activity could have pushed the file to the limit and triggered the NOSPACE alarm, even though the real data was small.
The fix
The team defragmented the two followers one after the other, then the leader last. Each took under a minute on solid-state disks. The files dropped to about 1.5 GiB each, restoring more than 6 GiB of headroom under the quota.
The prevention
They added an alert on fragmentation ratio and file size, moved Events into a separate etcd, and fixed the workload that caused the storm.
The dangerous moment was not during the storm. It was the quiet morning after, when everything looked fine on a dashboard that watched the wrong number. Always chart file size and in-use size together.
23Real-World and Industry Examples
The same two jobs appear across the industry, from managed services to the largest self-run clusters.
Managed Kubernetes on the major clouds
Google Kubernetes Engine, Amazon EKS and Azure AKS operate etcd on your behalf. You never run these commands yourself, but the providers do the same maintenance in the background. When a provider mentions limits on object counts or sizes, it is protecting exactly this database.
Self-managed clusters with kubeadm
A cluster built with kubeadm runs etcd as a static Pod on each control plane node. The API server compacts by default, but nothing defragments automatically. Teams commonly add a scheduled job or a tool such as etcd-defrag, together with the alert rules that ship with etcd.
OpenShift
Red Hat’s OpenShift includes an etcd operator that watches member health and automatically defragments members when fragmentation passes a threshold. It is a good example of turning the manual runbook of this guide into a controller.
Large-scale operators
Organisations running thousands of nodes have publicly described isolating Kubernetes Events in their own etcd cluster, placing etcd on fast local solid-state disks, and watching write rates closely. OpenAI’s write-up about scaling Kubernetes to thousands of nodes is a widely read example of these practices.
Heritage and governance
etcd was created at CoreOS and is now a graduated project of the Cloud Native Computing Foundation. Its maintainers publish operational guidance on maintenance, including compaction, defragmentation and the space quota, which is the basis for the practices in this guide.
Every mature team ends up with the same trio: automatic compaction, threshold-driven defragmentation and alerts on both size numbers. The details differ, the shape does not.
24Advanced Topic: Inside the bbolt Storage Engine
To truly understand why files do not shrink, it helps to look one level below etcd at the storage library it relies on.
A tree of pages in one file
bbolt stores data as a B+tree, a tree-shaped structure that keeps keys in sorted order so that lookups and range reads are fast. The tree is built out of fixed-size pages, usually the same size as a page of the operating system, commonly four kibibytes. Small pages near the top of the tree point to pages further down, and the actual data sits in the leaf pages at the bottom. Everything lives inside one single file.
Copy-on-write: never edit, always replace
When a value changes, bbolt does not edit the page in place. It writes a new copy of the changed page, and of every page above it in the tree that needs to point to the new copy, and then switches the root to the new version. The old pages are not erased. They are marked as free once no reader can still be using them. This is called copy-on-write, and it has a great benefit: a crash in the middle of a write cannot damage the existing tree, because the old version is still intact until the switch is complete. That safety is the reason etcd uses it.
Imagine a town directory printed as a book. When an address changes, a careful editor never scribbles over the page. They reprint that page and every index page that refers to it, then swap the new set into the binder in one move. If the power fails mid-reprint, the old binder is still complete and correct. The cost is that a stack of discarded pages builds up beside the binder.
One writer, many readers
bbolt allows a single writer at a time and any number of readers. Readers see a consistent picture of the tree as it was when they began, and are never blocked by the writer. The pages that a long-running reader might still be looking at cannot be reused yet. In practice this means a very long read can delay the reuse of free pages, which is one more reason to avoid holding enormous reads open in etcd.
How etcd lays its data on top
etcd does not use the user’s key directly as the tree key. Inside the main bucket, the tree key is the revision, and the value holds the user key together with the data. Separately, etcd keeps an in-memory index that maps each user key to the list of revisions at which it has existed. This design explains several behaviours.
- Writing is fast, because new revisions are always larger and are appended at the right-hand end of the tree.
- Reading the latest value of a key means looking up the key in the in-memory index, finding its newest revision, and fetching that revision from the tree.
- Compaction has two parts: pruning the in-memory index, and deleting the old revision entries from the tree. Only the second part creates free pages.
- A key’s life from creation to deletion is called a generation, and compaction can drop whole generations of keys that have been deleted.
Batching writes to disk
To avoid flushing to the disk after every single change, etcd groups writes and commits them to the file in batches, roughly ten times a second or whenever a batch limit is reached. The durability of an individual write is already guaranteed by the write-ahead log. The batch commit is what turns many small changes into one disk operation. The time this commit takes appears in the backend commit duration metric and is a good indicator of storage health.
Why the file cannot give pages back
The file is also memory-mapped, which means a region of the process’s address space mirrors the file. Free pages sit scattered through that region. Returning a middle page to the operating system would punch a hole in a structure that the engine assumes is contiguous. The designers chose simplicity and safety: the file only grows, free pages are tracked in a list, and the only way to compact the physical layout is to build a fresh file. That is precisely what defragmentation does.
When you think “why is my file still big?”, picture the town directory binder with the stack of discarded pages beside it. Compaction removes the pages from the binder. Defragmentation prints a new, thin binder.
25Advanced Topic: Raft, Revisions and Why Compaction Is Replicated
Compaction looks simple, but it must give the same answer on every member. Raft is how that is guaranteed.
One log, applied in the same order everywhere
Raft keeps an ordered log of operations. A write is proposed to the leader, copied to a majority of members, and then applied to each member’s database in exactly the same order. Because every member applies the same operations in the same order, they all arrive at the same revision numbers. Revision 5000 means the same change on every member. That determinism is what makes revisions a safe thing for clients to rely on, even when they switch between members.
Compaction travels through the same log
A compaction request is also an operation in the log. When it reaches a member’s turn to be applied, the member removes the old revisions up to the chosen point. Since every member does it at the same logical position in the log, every member forgets exactly the same history. If compaction were applied independently on each member at different times, a client could be told “your revision is fine” by one member and “your revision is gone” by another. Replication avoids that confusion.
Defragmentation stays outside the log
Defragmentation changes only the physical layout of one member’s file. The logical contents are identical before and after, so there is nothing for the other members to agree about. It is therefore performed locally and is not part of the log. This is why you must repeat it on every member, and why a successful defragmentation of one member says nothing about the others.
flowchart LR
REQ["Compact request for revision N"] --> L["Leader appends to Raft log"]
L --> F1["Follower 1 receives entry"]
L --> F2["Follower 2 receives entry"]
F1 -->|"acknowledge"| L
F2 -->|"acknowledge"| L
L -->|"majority reached"| A0["Leader applies compaction"]
F1 --> A1["Follower 1 applies compaction"]
F2 --> A2["Follower 2 applies compaction"]
DF["Defragmentation request"] -.->|"local only, not in the log"| A1
What happens to a member that falls behind
A member that is paused, for example during defragmentation, falls behind on applying new log entries. When it resumes, the leader either sends the missing entries or, if the member is too far behind for the leader to still hold them, sends a full snapshot. A snapshot transfer is slower and larger when the database file is bloated. A neat side effect is that a member rebuilt from a snapshot ends up with a compact file, because it starts fresh from live data.
Why keeping members similar helps
If one member has a much bigger file than the others, it also takes longer to defragment, longer to restore and longer to ship in a snapshot. It is wise to treat all members the same way in your maintenance plan, even though only one at a time is paused.
Consistency checks
Because every member applies the same operations, members can compare a hash of their data at a given revision to prove they are identical. Recent etcd versions can check this after compaction and raise an alarm if a member has diverged. It is a strong safety net against rare storage corruption, and it works only because compaction is applied at a precisely agreed point.
26Settings Explained: Where Each Knob Lives
Several settings influence this topic. Knowing which component owns each one prevents contradictory choices.
| Setting | Owned by | What it controls | Notes |
|---|---|---|---|
| Compaction interval | kube-apiserver | How often the API server asks etcd to compact | Default is about five minutes. Setting it to zero turns the API server’s compaction off. |
| Auto-compaction mode | etcd | Whether etcd compacts by time window or by revision count | Off by default. Choose either time-based or revision-based, never rely on both and the API server together. |
| Auto-compaction retention | etcd | How much history to keep: a duration in time mode or a count in revision mode | Works together with the mode above. |
| Backend quota | etcd | The maximum size of the database file | Default is two gibibytes, suggested ceiling is eight. |
| Maximum request size | etcd | The largest single request accepted | Default is around one and a half mebibytes, which also limits object sizes. |
| Heartbeat interval and election timeout | etcd | How fast failures are detected and how long a leader may be silent | Defaults are 100 milliseconds and 1000 milliseconds. Long pauses can exceed them. |
| Event time to live | kube-apiserver | How long Events are kept | Default is about one hour. Shorter values reduce stored volume. |
| Server overrides for resources | kube-apiserver | Sends chosen resource types to a different etcd | Standard way to isolate Events. |
| Data directory | etcd | Where the file, logs and snapshots live | Place on a dedicated fast disk. |
Two auto-compaction modes of etcd
Periodic
Keeps a fixed window of time, for example the last hour. Predictable in terms of recovery windows, but the amount of history depends on write rate.
Revision
Keeps a fixed number of revisions, for example the last thousand. Predictable in terms of disk size, but the time window shrinks when writes are busy.
Choosing between them
In a Kubernetes cluster the API server already provides periodic-style compaction by default, so the simplest choice is to leave etcd’s own auto-compaction off and let the API server own the job. In an etcd cluster used by other applications as well, or where the API server’s compaction has been turned off, etcd’s own setting is the right place. Whichever you pick, write it down in your runbook so that the next engineer does not enable a second compactor and wonder about the extra activity.
Turning off every compactor by accident. A migration to a new installer, or an attempt to “reduce load”, can leave neither the API server nor etcd compacting. The database then grows silently until the alarm appears. Verify the end state, not just the intention.
27Special Situations: Offline Maintenance, Restores and Upgrades
Offline defragmentation
Besides the online command, newer etcd releases include a separate utility meant for operations on files while the member is stopped. It can defragment a data directory offline. This is useful when a member is so full that it cannot start or cannot serve the online command, or when you prefer to perform a long rewrite while the member is out of the cluster. Stop the member cleanly, run the offline step against its data directory, and then start it again so that it catches up from its peers.
Restoring from a snapshot
A snapshot restore builds a new data directory from the saved file. The result is a fresh, compact database. It also starts a new cluster identity, so restoring a whole control plane means restoring every member from the same snapshot, then restarting the API servers so that they reconnect. Two consequences are worth knowing.
- Revisions can go backwards. The restored database continues from the revision at the time of the snapshot, which may be lower than revisions that clients saw before the failure. Some clients then hold resource versions from the “future”. Newer etcd tools can raise the revision after a restore so that this does not confuse clients, and following current guidance for your version is wise.
- Caches must be refreshed. The API server and its clients should relist after a restore. Restarting the control plane components achieves this.
Upgrades
Upgrading etcd is done one member at a time, like defragmentation, and for the same reason: quorum must stay. Do not combine an upgrade with a defragmentation of the same member in the same window. Separate changes make a problem easy to attribute. After a successful upgrade, there is normally no need to defragment unless the numbers call for it.
Disaster recovery planning
Decide the recovery goals
How much data loss, measured in time, can you accept, and how long can the cluster be without writes?
Schedule backups to match
If you can lose at most fifteen minutes of changes, back up at least that often.
Store copies off the machine and off the site
A backup on the same disk as the data does not survive the disk.
Rehearse a full restore
Measure the time and write down every command. The first time should not be during an outage.
Keep the file compact
A defragmented file means faster backups, faster copies and faster restores.
Many clusters, one routine
Organisations with dozens of clusters cannot maintain each one by hand. They package the runbook as automation that reads health and fragmentation, acts only when thresholds are met, staggers work so that not all clusters pause on the same minute, and reports results to a central place. The principles in this guide are the same at any scale. Only the amount of repetition changes.
28Cost and Capacity Economics
Keeping etcd lean is not only about safety. It also saves money and time.
What a bloated file really costs
| Item | Effect of a bloated file | Effect of a lean file |
|---|---|---|
| Disk provisioned | More capacity needed on every control plane node | Smaller volumes suffice |
| Memory needed for the mapped file | Larger machines, or slower reads | Smaller machines with fast reads |
| Backup size and storage | Each snapshot is as large as the file | Snapshots only as large as live data |
| Backup and copy time | Longer, so recovery points drift apart | Short, so frequent backups are practical |
| Restore time | Longer outage during a disaster | Shorter outage |
| Member recovery after failure | Slow snapshot transfer from the leader | Fast catch-up |
| Risk of hitting the quota | High, even with small live data | Low, large headroom |
A simple calculation
Suppose a cluster keeps its snapshots for thirty days, taking one every hour. That is about seven hundred and twenty snapshots. With a bloated file of six gibibytes, the archive holds more than four terabytes. With a lean file of one and a half gibibytes, the same archive is about a terabyte. Even before counting transfer costs between sites, the saving is substantial. Compression helps both cases, but it cannot hide a four-to-one difference in the raw size.
The price of the maintenance itself
Defragmentation costs a few minutes of an engineer’s attention, or nothing at all once automated, and a few seconds of one member’s availability. Compared with the costs above, and with the cost of even a short outage caused by the quota, it is among the cheapest forms of reliability work available to a platform team.
What not to optimise
- Do not shorten the compaction interval to extremes to save a few megabytes. Clients need a little history to catch up.
- Do not run defragmentation on a small, healthy file just to see the numbers fall. It adds risk for no benefit.
- Do not spend weeks tuning flags when a single misbehaving controller is the real source of the growth.
Put three numbers on one dashboard that leadership can read at a glance: file size as a share of the quota, fragmentation ratio and time since the last successful backup. They tell the whole health story of the cluster’s memory in one place.
29Glossary of Terms
| Term | Meaning in plain words |
|---|---|
| etcd | The reliable key-value database where Kubernetes stores all cluster state |
| Key and value | A name and the data saved under that name |
| Revision | A number that increases with every change in the database |
| MVCC | Storing many versions of each key rather than overwriting |
| Tombstone | A marker recording that a key was deleted |
| Compaction | Forgetting old versions older than a chosen revision |
| Defragmentation | Rewriting the database file to remove free gaps and return space to the disk |
| Fragmentation | Free space scattered inside the file |
| bbolt | The storage library that manages etcd’s database file |
| Page | A fixed-size block of the database file |
| Freelist | The list of free pages that can be reused |
| Backend quota | The maximum allowed size of the database file |
| NOSPACE alarm | The flag raised when the quota is reached, after which only reads and deletes work |
| Raft | The agreement protocol used by etcd members |
| Member | One etcd server in the group |
| Leader | The member that orders writes |
| Quorum | The majority of members needed to accept a write |
| Write-ahead log (WAL) | A sequential log used for crash recovery |
| Snapshot | A point-in-time backup copy of the database |
| Watch | A live stream of changes from a given revision |
| Watch cache | The API server’s in-memory copy of recent changes |
| resourceVersion | The Kubernetes field derived from the etcd revision |
| Relist | A client fetching the full current state again after its history is lost |
| Events | Short-lived Kubernetes objects describing what happened to other objects |
| Lease | A small heartbeat object renewed on a timer |
30Frequently Asked Questions
Because compaction frees space only inside the file. The file keeps its high-water mark size. Defragmentation is the only normal way to return that space to the disk and bring the file back under the quota margin.
No. It removes only old versions and deleted history. The latest value of every live key is always kept.
No. It copies all live data into a new file and swaps it in. Take a backup first anyway, as with any maintenance.
Running workloads are not affected. The control plane may see brief slowness while one member is paused. Done one member at a time on a healthy cluster, applications normally notice nothing.
As often as the measurements say. When the file is large and a big share of it is free, defragment. In a well-behaved cluster that may be rarely.
Each member keeps its own file, shaped by its own history of writes, restarts and defragmentations. Differences are normal.
The write-ahead log is the recovery diary, trimmed by snapshots. Compaction removes old key versions inside the database. They are separate mechanisms with separate purposes.
You can, within the supported range, but it hides the symptom rather than fixing it. Larger files are slower to back up, restore and map into memory.
The NOSPACE alarm does not clear itself. You must disarm it explicitly after compaction and defragmentation, then confirm that a write succeeds.
Not by itself. A delete adds a new revision. The space is only freed inside the file after compaction and returned to the disk after defragmentation.
Usually no, since the provider operates etcd. Your best contribution is to avoid runaway writes and very large objects, which reduces load on the shared design limits.
Compaction is. Defragmentation is usually safe on a healthy cluster with proper sequencing, but a quiet window is wiser, particularly for the leader.
31Summary and Key Takeaways
What to remember
- etcd keeps history. Every change creates a new revision, and deletes only add tombstones. That is why the database grows even when you remove objects.
- Compaction forgets, it does not shrink. It removes old revisions and turns their pages into free space inside the file, but the file stays the same size.
- Defragmentation gives space back. It rewrites the file with only live data and returns the rest to the operating system. It runs per member and briefly pauses that member.
- The quota counts the file. A bloated file can hit the limit and freeze writes cluster-wide even when real data is small. Recovery is compact, defragment every member, then disarm the alarm.
- Kubernetes is built for compaction. Clients relist when their history is gone, so frequent compaction is safe, but long pauses can cause a burst of relists.
- Operate with discipline. Back up first, one member at a time, followers before the leader, verify between steps, and automate with safeguards.
- Watch two numbers, fix the writers. Chart file size and in-use size together, alert on fragmentation, and reduce the heavy writers such as Events, leases and noisy controllers.