Controller Quorum & Raft Consensus in Kafka

Controller Quorum & Raft Consensus in Kafka

How Kafka replaced ZooKeeper with its own built-in agreement protocol, and how a small group of controller nodes votes, copies a shared log, takes snapshots, and recovers from failure so the whole cluster always agrees on who is in charge of what.

Imagine a school with hundreds of students, dozens of classrooms, and a big wall chart that says which teacher is responsible for which class today. If two teachers each keep their own copy of that chart and the copies drift apart, chaos follows: two teachers walk into the same room, and another room has nobody. To avoid this, the school picks a small committee of three or five senior teachers. They keep one official notebook, they only write a new line in it when most of them agree, and one of them acts as the chair who holds the pen. Everyone else in the school simply reads the notebook. If the chair falls sick, the committee quickly votes for a new chair, and the notebook is never lost.

Apache Kafka works in almost exactly this way today. The wall chart is the cluster metadata, the committee is the controller quorum, the notebook is the metadata log, and the voting rules are a consensus protocol called Raft. This tutorial walks through every part of that picture, slowly and from the ground up, so that by the end you can explain it to a colleague, design a production deployment, and debug it at three in the morning.

1Why Kafka Needed a New Brain

To understand the controller quorum, first understand the problem it was built to solve.

What “metadata” actually means

A Kafka cluster stores your messages in topics, and every topic is split into partitions. But besides the messages themselves, the cluster must remember a large amount of information about itself. Which brokers are alive right now? Which topics exist? How many partitions does each topic have? Which broker is the leader of each partition, and which brokers hold up-to-date copies? What are the configuration overrides, the quotas, and the access control rules? All of this “data about data” is called metadata.

Metadata is small compared to message data, but it is extremely important. If brokers disagree about who leads a partition, two brokers might both accept writes for it and your data would diverge. So Kafka needs one trusted place that stores the metadata and one trusted process that changes it.

Simple Analogy

Think of a library. The books are your messages. The card catalog, which tells you which shelf each book sits on, is the metadata. You can lose a few books and survive, but if the card catalog is wrong or two librarians keep different catalogs, nobody can find anything at all.

The ZooKeeper era

For its first decade, Kafka kept its metadata in a separate system called Apache ZooKeeper, a small coordination service that runs as its own group of servers. One of the Kafka brokers was chosen as the controller by racing to create a special temporary entry in ZooKeeper. The winner became the controller, watched ZooKeeper for changes, made decisions such as electing new partition leaders, and then pushed those decisions to every other broker through direct network calls.

This design worked, and it powered huge deployments for years. But as clusters grew, its weaknesses became painful.

Pain 1

Two systems to run

Operators had to deploy, secure, monitor, patch, and capacity-plan two different distributed systems with different tools, ports, and failure modes.

Pain 2

Slow controller failover

When the controller broker died, the new controller had to read the full cluster state from ZooKeeper before doing anything. On big clusters this took many seconds or even minutes.

Pain 3

A scaling ceiling

Every partition added more entries in ZooKeeper and more messages pushed to brokers. Clusters with hundreds of thousands of partitions struggled with startup time and recovery time.

Pain 4

Inconsistent views

The controller pushed updates to brokers over RPC calls. If a push was lost or delayed, a broker could hold a stale picture of the cluster with no simple way to notice.

Pain 5

Split security model

ZooKeeper had its own authentication and authorization story separate from Kafka’s, so securing the whole stack meant doing the work twice.

Pain 6

Split brain risk

A broker’s view of who the controller was could differ from ZooKeeper’s view for a short time, which made some rare failure cases hard to reason about.

!
Common Misconception

People often say ZooKeeper was “slow”. It was not the raw speed of ZooKeeper that hurt. The real issue was the architecture: the controller kept a copy of state in memory, ZooKeeper kept another copy, and the brokers kept a third. Keeping three copies aligned through push messages was the fragile part.

2 → 1
Systems an operator must run
KIP-500
The proposal that started the journey
4.0
First major release without ZooKeeper support

The road to KRaft

The Kafka community answered these problems with a proposal known as KIP-500, “Replace ZooKeeper with a Self-Managed Metadata Quorum”. The idea was bold and simple: stop storing metadata in an outside system. Instead, treat metadata as just another log, the very thing Kafka is best at, and let a small set of Kafka nodes agree on that log using a consensus protocol. The result is called KRaft, short for “Kafka Raft”.

1

Proposal (2019)

KIP-500 lays out the vision of a self-managed metadata quorum and a metadata log stored inside Kafka itself.

2

Early access (2021)

Kafka 2.8 lets brave users run a cluster without ZooKeeper for the first time, strictly for experiments.

3

Production-ready for new clusters (2022)

Kafka 3.3 declares KRaft ready for production use in new clusters.

4

Migration and deprecation (2023 to 2024)

ZooKeeper mode is deprecated, and a careful live-migration path lets existing clusters move to KRaft without downtime.

5

ZooKeeper removed (2025)

Kafka 4.0 ships with KRaft as the only mode, and adds the ability to change the controller group while the cluster is running.

2The Big Picture: What Is KRaft?

One sentence summary: a few Kafka nodes keep a replicated log of cluster decisions, and every broker reads that log.

The three-part idea

KRaft rests on three ideas that fit together neatly.

First, metadata is an event log. Instead of storing “the current state” in a database, Kafka records every change as an event: “topic orders was created”, “broker 4 registered”, “partition 7 of orders now has leader broker 2”. The current state of the cluster is simply what you get by replaying all those events in order.

Second, a small group of nodes owns the log. These nodes are called controllers. They replicate the log among themselves and use the Raft consensus algorithm so that they always agree on its exact contents, even when machines crash or networks misbehave.

Third, everyone else follows the log. Regular brokers do not vote. They simply pull new events from the controllers and apply them to their own in-memory picture of the cluster.

Simple Analogy

Picture a group chat that only three admins can post in. Every decision, like “room 5 now belongs to Ms. Rao”, is a message in that chat. Everybody else in the school is a silent member who scrolls the chat. Because the chat is an ordered list, anyone who scrolls from the top always reaches the same final picture.

flowchart TD
    subgraph Quorum["Controller quorum - voters"]
        C1["Controller 1 - active leader"]
        C2["Controller 2 - follower"]
        C3["Controller 3 - follower"]
    end
    C1 --- C2
    C1 --- C3
    C2 --- C3
    B1["Broker 1 - observer"]
    B2["Broker 2 - observer"]
    B3["Broker 3 - observer"]
    B4["Broker 4 - observer"]
    B1 -->|"fetch metadata"| C1
    B2 -->|"fetch metadata"| C1
    B3 -->|"fetch metadata"| C1
    B4 -->|"fetch metadata"| C1
    P["Producers and consumers"] -->|"data traffic"| B1
    P -->|"data traffic"| B2
        
FIG 1 — Controllers vote and own the metadata log, brokers read it, clients talk only to brokers

Notice that producers and consumers never talk to controllers for their normal work. The controllers are the “brain”, and the brokers are the “hands”. Clients keep working with brokers, and brokers ask the controllers for decisions only when something about the cluster itself changes.

What lives in the metadata log

The metadata log is stored as an ordinary Kafka topic-style log with one partition. Its internal name is __cluster_metadata. Each entry in it is a small record with a type. Some examples are records that register a broker, records that create or delete a topic, records that change partition leadership or the set of in-sync replicas, records that store configuration and quota changes, records that hold access control entries, and records that store feature versions.

Every record has an offset, which is just its position in the log: 0, 1, 2, and so on. Offsets give the cluster a shared clock. When a broker says “I have applied the metadata up to offset 48,210”, every other node knows exactly what that broker knows.

i
Key Insight

Because state is derived from an ordered log, “how up to date is this broker?” becomes a single number: the offset it has applied. That single idea replaces a huge amount of fragile push-notification logic from the ZooKeeper era.

3Core Vocabulary You Need

Every new word below is explained once here and then used consistently for the rest of the tutorial.

Term 1

Controller

A Kafka node configured to take part in the metadata quorum. It stores the metadata log and can vote in elections.

Term 2

Voter

A node whose vote counts. The full list of voters is the quorum. Only voters can become leader.

Term 3

Observer

A node that reads the log but never votes and never counts toward a majority. Every ordinary broker is an observer.

Term 4

Active controller

The single controller that is currently the Raft leader. It is the only node that makes changes to the cluster.

Term 5

Standby controller

A voter that is a follower. It copies the log and replays it, so it is ready to take over almost instantly.

Term 6

Epoch

A number that increases every time a new leader election begins. It works like a reign number for leaders and is used to reject old leaders.

Term 7

High watermark

The highest log offset that a majority of voters have safely stored. Anything at or below it is committed and can never be lost.

Term 8

Snapshot

A compact picture of the whole cluster state at one offset, so old log entries can be thrown away and new nodes can catch up fast.

Term 9

Majority

More than half of the voters. With 3 voters it is 2, with 5 voters it is 3. Every important decision needs a majority.

Term 10

Broker epoch

A number given to a broker each time it registers, used to tell apart the current life of a broker from an older crashed one.

i
Naming Note

Classic Raft papers say “term” for the reign number. Kafka’s code and documentation say “epoch” because Kafka already uses that word for partition leaders. They are the same idea, so when you read about Raft elsewhere, translate term to epoch.

4Architecture and Components

A KRaft cluster has just a few moving parts. Knowing each one makes everything else easy.

Process roles

Every Kafka node declares what job it does through a setting called process.roles. The value can be broker, controller, or both. A node with only the controller role stores the metadata log and takes part in elections but does not host any topic data. A node with only the broker role serves clients and follows the metadata log. A node with both roles does both jobs in one process, which is called combined mode.

ModeWhat runs on the nodeBest used for
Dedicated controllerOnly the quorum and metadata logProduction clusters of any serious size
Broker onlyTopic data and client traffic, follows metadata as an observerEvery production broker
CombinedBoth jobs in one processLaptops, development, small tests, and edge setups
Simple Analogy

A dedicated controller is like a full-time school principal who does nothing but manage. Combined mode is a teacher who also serves as principal: fine in a tiny school, risky in a big one because a heavy teaching day slows down management.

The components inside a controller

1

Raft manager

Runs the election logic, tracks the current epoch and leader, sends and answers vote requests, and decides what counts as committed.

2

Metadata log

The durable, ordered list of records stored on disk, with snapshots stored next to it.

3

Quorum state file

A tiny file that remembers the current epoch, who this node voted for, and the known leader, so a restart never lets a node vote twice in the same epoch.

4

Controller state machine

The logic that turns client requests such as “create this topic” into metadata records. Only the active controller uses it to produce new records, while standbys use it to replay committed records.

5

Metadata image and delta

An in-memory picture of the full cluster built by replaying the log, plus small “delta” objects that describe each change so listeners can react cheaply.

6

Controller listener

A dedicated network endpoint, named by controller.listener.names, used for voting, replication, and broker-to-controller calls, kept separate from client traffic.

How nodes find each other

In the classic static setup, every node is given the exact list of voters through controller.quorum.voters, written as a set of node id, host, and port entries. Newer versions also support controller.quorum.bootstrap.servers, which is just a list of addresses used to discover the current quorum, and this is what makes changing the quorum at runtime possible. Each node also has a unique node.id and, before first use, the storage directory must be formatted with a shared cluster id so that nodes from different clusters can never be mixed by mistake.

!
Common Mistake

Using different cluster ids on different controllers, or reusing a data directory from another cluster, leads to nodes that refuse to talk to each other. The format step exists precisely to stamp one identity on the whole cluster, so always format every node with the same id.

5Consensus 101: Why Agreement Is Hard

Before Raft makes sense, you need to feel the problem it solves.

The problem in plain words

Suppose three computers must all remember the same list of decisions. Computers can crash, restart, freeze for a few seconds, or lose their network cable. Messages between them can be delayed, duplicated, or dropped. Yet the list must never contain two different versions of the same decision, and a decision that was announced as final must never disappear. This challenge is called distributed consensus.

Simple Analogy

Three friends in three different cities must agree on where to meet for dinner, but they can only send letters, and some letters get lost. If two friends agree on the pizza place while the third never gets the letter and picks the sushi place, the group splits. Consensus protocols are the careful rules that stop this.

Two safety promises

Promise 1

Agreement

No two nodes will ever believe in different values at the same position in the log. There is one history, not several.

Promise 2

Durability of committed entries

Once an entry is announced as committed, every future leader will contain it. Committed decisions survive crashes and elections.

Why a majority solves it

Here is the trick that makes Raft work. Any decision requires the approval of a majority. Any two majorities of the same group must overlap in at least one member. That overlapping member has seen both decisions, so it can prevent two conflicting decisions from both succeeding. It is the same reason two different winning coalitions in an election can never both include entirely different sets of voters.

VotersMajority neededFailures toleratedComment
110No fault tolerance, development only
220Worse than 1, since losing either node halts writes
321The common production choice
431No better than 3, just more machines
532Used for very high availability needs
743Rare, adds coordination cost
“An even number of voters buys you extra hardware but not extra safety.”

The three jobs of Raft

Raft breaks consensus into three understandable jobs. Leader election chooses one node to be in charge. Log replication makes that leader copy its log to the others. Safety rules make sure that only a node with the most complete log can win an election, so committed entries are never lost. The next chapters show how Kafka performs each job in its own particular style.

6How Kafka’s Raft Differs from Textbook Raft

Kafka did not copy Raft blindly. It bent the protocol to match the tools Kafka already trusts.

Push versus pull

In the original Raft paper, the leader pushes new entries to followers with a message usually called “append entries”. Kafka’s version turns this around. Followers pull data from the leader by sending ordinary fetch requests, exactly like a Kafka consumer or a Kafka replica follower does. The leader never has to remember what each follower has or chase after it.

Simple Analogy

Push is a teacher walking around the room dictating notes to each student. Pull is students walking up to the teacher’s desk and saying “I have notes up to line 40, what comes next?” The second style scales more calmly, because a slow student cannot block the teacher, and the teacher does not keep a list of every student’s progress.

AspectTextbook RaftKafka Raft (KRaft)
Replication directionLeader pushes entries to followersFollowers and observers pull with fetch requests
Name for leader reignTermEpoch
HeartbeatSeparate empty append messagesRegular fetch requests double as proof of life
Non-voting membersOptional extensionFirst class, brokers are observers
Log divergence repairLeader steps back one entry at a timeFetch response tells follower the exact epoch and offset to truncate to
SnapshotsLeader pushes snapshot chunksFollower pulls snapshot through a dedicated fetch snapshot request
Storage layerCustom logReuses Kafka’s own log segments and indexes
Membership changesJoint consensus or single-server changesSingle-server changes, recorded in the log itself

Why pull made sense for Kafka

Reuse of battle-tested code

Kafka already had years of engineering behind fetch-based replication, log segments, and epoch-based truncation. Reusing them meant less new code to trust.

Natural support for observers

Because any node can simply issue fetches, thousands of brokers can follow the metadata log without joining the voting group and without slowing votes down.

Leader stays simple

The leader does not track a per-follower “next index” and does not retry pushes. It just answers fetches and watches how far each voter has read to work out what is committed.

i
Design Note

The safety proof of Raft does not depend on whether data moves by push or pull. Kafka’s design keeps the same safety rules, majority approval, epochs, and “most complete log wins”, so it inherits the same guarantees.

7Leader Election Step by Step

Elections are how the quorum picks the one node allowed to write to the metadata log.

The states a controller can be in

At any moment, a voter is playing one of a few roles. It helps to think of them as costumes that a node changes as events happen.

State

Unattached

The node knows the current epoch but does not know a leader yet and has not voted. This is the state right after startup or after hearing about a new epoch.

State

Follower

The node knows who the leader is and keeps fetching log entries from it. Most of a healthy controller’s life is spent here.

State

Voted

The node has cast its vote for a candidate in the current epoch. It remembers this so it can never vote twice in the same epoch.

State

Candidate

The node has raised its hand to become leader. It has increased the epoch, voted for itself, and asked the others for votes.

State

Leader

The node won a majority of votes. It now accepts writes, and it informs everyone that a new epoch has begun.

State

Resigned

A leader that is stepping down, for example during a graceful shutdown, tells the others so they can start an election without waiting for a timeout.

What triggers an election

A follower expects to hear from the leader regularly, because it keeps sending fetch requests and getting answers. If the leader stops responding for longer than the fetch timeout, which defaults to a couple of seconds, the follower assumes the leader is gone. It then waits for a small random delay and becomes a candidate. The random delay is called the election backoff, and it is the secret that prevents all followers from becoming candidates at the exact same moment and splitting the vote.

Simple Analogy

When a teacher goes silent in a classroom of students who are used to hearing instructions, the students do not all jump up together. Each waits a slightly different moment, and the first one to stand up says “shall I lead?” Because they waited different amounts, usually only one stands first.

The election in five steps

1

Timeout and backoff

A follower stops hearing from the leader, waits a random short period, then decides to run.

2

Become candidate

It increases the epoch by one, writes down that it voted for itself, and saves this to its quorum state file before sending anything.

3

Ask for votes

It sends a vote request to every other voter, including the epoch and the end of its own log: the epoch and offset of its last entry.

4

Voters decide

Each voter grants its vote only if the candidate’s epoch is new enough, it has not already voted for someone else in that epoch, and the candidate’s log is at least as complete as its own.

5

Majority wins

Once the candidate has a majority, it becomes leader and sends a begin-quorum-epoch message so everyone learns of the new leader and starts fetching from it.

sequenceDiagram
    participant C1 as Controller 1 (old leader, down)
    participant C2 as Controller 2
    participant C3 as Controller 3
    Note over C2,C3: Fetches to Controller 1 time out
    Note over C2: Random election backoff expires first
    C2->>C2: Epoch 5 to 6, vote for self
    C2->>C3: Vote request (epoch 6, last log epoch and offset)
    C3->>C3: Check epoch, no earlier vote, candidate log is complete enough
    C3-->>C2: Vote granted
    Note over C2: 2 of 3 votes is a majority
    C2->>C3: Begin quorum epoch 6, I am leader
    C3->>C2: Fetch from offset N
    Note over C2,C3: Controller 1 later returns, learns epoch 6, becomes follower
        
FIG 2 — A successful election after the leader crashes

The “most complete log wins” rule

This rule is the heart of Raft’s safety. A voter refuses to support a candidate whose log is behind its own. Since a committed entry has been stored on a majority, and a winning candidate needs a majority of votes, at least one voter in every winning vote has the committed entry. That voter will refuse anyone who lacks it. So a candidate that is missing committed entries can never win.

!
Split votes

Sometimes two candidates start at nearly the same moment and each gets one vote out of three. Nobody wins that epoch. Nothing is broken: the candidates simply time out, wait another random delay, and try again in a higher epoch. This retry loop is normal and usually resolves within one or two rounds.

Graceful stepping down

When an operator stops the active controller in a planned way, the leader sends an end-quorum-epoch message that names the preferred successors. The other voters can then start an election immediately instead of waiting for a full timeout. This is why planned restarts of controllers cause a much smaller pause than crashes do.

8Epochs and Fencing: Stopping Old Leaders

The epoch is the single most important safety device in the whole protocol.

What an epoch does

Every message between controllers carries an epoch number. The rule is short and strict: if you receive a message with an epoch lower than your own, you reject it and tell the sender the current epoch. If you receive a message with a higher epoch, you immediately accept that a newer era has started, and you adjust yourself. That is all. But this tiny rule blocks a surprising number of dangerous situations.

Simple Analogy

Think of a mayor’s official stamp that changes color with each new mayor. If someone shows up with an order stamped in last year’s color, clerks know it comes from an old mayor and ignore it, even if the paper looks perfectly genuine.

The zombie leader problem

Imagine the leader is not dead, only frozen for a few seconds by a long garbage collection pause or a network hiccup. The others give up, elect a new leader in a higher epoch, and continue. Then the frozen leader wakes up and still believes it is in charge. If it tried to write, we would have two leaders. This is dangerous, and it is called a zombie leader, or split brain.

Kafka’s design stops this in several layers:

Layer 1: Followers reject stale epochs

Followers now belong to the higher epoch, so they reject any fetch or data exchange that mentions the old epoch. The zombie cannot get anyone to acknowledge its writes.

Layer 2: No majority, no commit

A write is only committed after a majority stores it. Since the majority has moved on to the new epoch, the zombie can never gather enough acknowledgements. Its writes simply never become committed.

Layer 3: Leaders check they still have a quorum

A leader that has not heard from a majority of voters within the fetch timeout steps down by itself. So even before a rival is elected, a cut-off leader stops believing it is in charge.

Layer 4: Truncating uncommitted writes

When the old leader comes back and starts following the new one, any entries it wrote that were never committed are removed and replaced with the new leader’s history.

flowchart TD
    A["Leader in epoch 5 freezes"] --> B["Others time out and elect new leader in epoch 6"]
    B --> C["Old leader wakes up, still thinks epoch 5"]
    C --> D["Old leader tries to replicate an entry"]
    D --> E{"Do followers accept epoch 5?"}
    E -->|"No, epoch too old"| F["Old leader receives epoch 6 in the rejection"]
    F --> G["Old leader becomes follower, drops uncommitted entries"]
    E -->|"Never happens"| H["Split brain avoided"]
        
FIG 3 — How a stale leader is fenced by higher epochs

Epochs also fence brokers and partitions

The same idea appears at other layers of Kafka. Every broker registration carries a broker epoch, so a broker that crashed and restarted cannot be confused with its former self. Every partition has a leader epoch, so a stale partition leader cannot get away with old decisions. Once you notice this pattern, Kafka’s design feels consistent: wherever there is a “who is in charge”, there is a number that grows with each new answer.

“In distributed systems, a bigger number always beats a smaller number, and that is what keeps the peace.”

9Log Replication and the High Watermark

This chapter follows one decision, from a request to a committed record that the whole cluster can trust.

The life of a write

Let us follow the creation of a new topic. An administrator sends a create-topic request to any broker. The broker forwards it to the active controller. The controller checks that the request is valid, decides how many partitions and which brokers should host replicas, and turns all of that into metadata records. Only then does the log come into play.

1

Validate

The active controller checks the request against its current in-memory state: does the topic already exist, are there enough brokers, are the settings legal?

2

Generate records

It produces a batch of metadata records that describe the change, without yet applying them as final.

3

Append to the leader log

The records are written to the leader’s own metadata log and stored durably on disk.

4

Followers fetch

The standby controllers send fetch requests, receive the new records, store them on their own disks, and report how far they have copied in their next fetch.

5

Advance the high watermark

The leader notices that a majority of voters, counting itself, have stored the records, and moves the high watermark up. The records are now committed.

6

Apply and reply

The controller applies the committed records to its state and answers the client. Followers and brokers learn the new high watermark through later fetch responses and apply the records too.

sequenceDiagram
    participant Admin as Admin client
    participant B as Any broker
    participant L as Active controller (leader)
    participant F1 as Standby controller 1
    participant F2 as Standby controller 2
    Admin->>B: Create topic request
    B->>L: Forward request
    L->>L: Validate and build metadata records
    L->>L: Append to own log
    F1->>L: Fetch from offset N
    L-->>F1: Records N to N+3
    F2->>L: Fetch from offset N
    L-->>F2: Records N to N+3
    F1->>L: Fetch from offset N+4 (proves it stored N to N+3)
    Note over L: Majority stored, high watermark moves to N+3
    L->>L: Apply records to state
    L-->>B: Success
    B-->>Admin: Topic created
    F2->>L: Fetch, learns new high watermark
        
FIG 4 — From a create-topic request to a committed metadata record

Why the fetch request doubles as an acknowledgement

Notice something clever in the diagram. A follower never sends a special “I got it” message. When the follower asks for the records starting at offset N+4, it is implicitly saying “I have safely stored everything before N+4”. The leader reads that number from each fetch and uses it to compute the majority position. The same message thus serves as a heartbeat, a request for data, and an acknowledgement. Fewer message types mean fewer places for bugs.

What “committed” really means

A record is committed when it is stored on a majority of voters. From that moment, the protocol guarantees it will appear in the log of every future leader. The high watermark is simply the offset of the last committed record. Observers, such as brokers, only ever apply records up to the high watermark, because anything beyond it might still be thrown away if the leader fails.

i
Durability matters

A vote or an acknowledgement is only meaningful if the data really reached stable storage. That is why controller disks should be reliable and have low write latency. A slow disk on a single voter slows the majority calculation whenever that voter is needed to reach two out of three.

Batching for speed

The controller does not write one record at a time. It gathers records into batches, and one batch can carry many changes, such as the creation of a topic with fifty partitions. Batching reduces the number of disk flushes and network round trips. This is one reason a KRaft controller can handle changes to very large numbers of partitions far faster than the old design.

10Log Divergence and Truncation

After a leader change, some nodes may hold entries that never made it to safety. Here is how they get cleaned up.

How logs drift apart

Suppose the leader in epoch 5 appends entries 100 to 103 to its own log, but crashes before any follower fetches them. A new leader is elected in epoch 6 from one of the followers, whose log ends at entry 99. The new leader then appends its own entries 100 and 101 with epoch 6. Now the old leader’s disk holds different data at positions 100 and 101 than the rest of the cluster. Those old entries were never committed, so throwing them away breaks no promise.

Simple Analogy

A student secretly writes four extra lines in the class notebook while the teacher is out. When the substitute teacher takes over and writes different lines on the same pages, the extra lines must be erased, since the class never officially agreed to them.

The fetch-based fix

When the returning node fetches from the new leader, its fetch request includes two extra facts: the offset it wants next, and the epoch of the last entry it holds. The leader compares this against its own log. If they match, it simply returns the next entries. If they do not match, the leader replies with a special note that says, in effect, “your log diverges from mine, and the last point where we agree is at this epoch and offset”. The follower truncates its log back to that point and fetches again from there.

flowchart TD
    A["Returning node sends fetch: next offset 104, last epoch 5"] --> B{"Does leader log agree at that position?"}
    B -->|"Yes"| C["Leader returns the next records"]
    B -->|"No"| D["Leader replies with the largest agreeing epoch and offset"]
    D --> E["Node truncates its log to that point"]
    E --> F["Node fetches again from the agreed offset"]
    F --> C
        
FIG 5 — Divergence detection and repair through fetch responses

This is the same technique Kafka uses for ordinary partition replication, which is why the code could be shared. It jumps to the correct point in one or two round trips instead of stepping back one entry at a time.

Why committed data is never truncated

The truncation only ever removes entries that were not stored on a majority. Committed entries exist on a majority, and the election rule guarantees the new leader has them, so the agreed point is always at or after the last committed entry. A follower will never be asked to delete something that was reported as committed.

ANTI-PATTERN-01 Avoid
Problem

An operator sees a controller with a “different” log after a crash and decides to copy the data directory from a healthy controller to “fix” it, or to delete the metadata log by hand.

Why It’s Harmful

The quorum state file and the log belong together and carry a node’s voting history. Copying them can make two nodes look identical, including their vote records, and deleting them can make a node forget votes it already gave. Either mistake can lead to two leaders in the same epoch or to lost committed data.

Correct Approach

Let the protocol heal itself. A node that comes back with extra or old entries will truncate or catch up automatically. If a disk is truly lost, follow the documented replacement procedure so that the node returns with a fresh identity and rejoins as a clean member.

11The Active Controller and the Metadata State Machine

Raft only agrees on a log of records. Something must give those records meaning. That something is the controller’s state machine.

A single-threaded brain

The active controller processes every request through a single event queue, one event at a time, on one thread. This may sound slow, but it is the opposite. Because only one thing ever changes the state at any moment, there are no locks, no race conditions, and no confusing partial states. The speed comes from batching many changes into each event and from keeping the whole state in memory.

Simple Analogy

A single cashier at a fast till who never leaves the register is often quicker than five cashiers arguing over one drawer of money. Everyone lines up, one transaction is finished completely, and then the next begins.

Two speeds of truth

The controller keeps two versions of the truth. The first is the latest state including uncommitted records, which it uses to validate the next request. This lets it accept a burst of requests without waiting for each one to be committed before checking the next. The second is the committed state, which reflects only what a majority has stored. The controller never answers a client with success until the relevant records are committed, so clients never see a decision that later vanishes.

If the leader loses its position, the uncommitted version is discarded and rebuilt from the committed log. This is safe, because nobody was ever told those changes had succeeded.

What the controller is responsible for

Duty 1

Broker membership

Tracking which brokers are registered, alive, fenced, or shutting down.

Duty 2

Topics and partitions

Creating, deleting, expanding, and placing partitions across brokers.

Duty 3

Partition leadership

Choosing a new partition leader when a broker fails, using the in-sync replica list.

Duty 4

In-sync replica changes

Accepting requests from partition leaders to shrink or grow their in-sync replica sets.

Duty 5

Configuration and quotas

Storing dynamic configuration, client quotas, and other cluster-wide settings.

Duty 6

Security data

Holding access control lists and delegation tokens in the log so every node sees the same rules.

Duty 7

Feature versions

Managing the metadata version and other feature levels so upgrades are coordinated and reversible when possible.

Duty 8

Producer id ranges

Handing out blocks of producer identifiers that brokers use for idempotent and transactional producers.

Standby controllers are always warm

Every voter, leader or not, replays committed records into its own in-memory image. So a standby is never cold. When the leader fails and an election completes, the new leader already knows the entire cluster state. It only needs to be careful about uncommitted entries at the tail of its log. This is the main reason controller failover dropped from many seconds in the old world to a very brief pause today.

i
Failover speed

In the ZooKeeper design the new controller had to load everything before doing any work. In KRaft the new controller already has everything loaded. The time to recover is mostly the election time, which is a matter of a few seconds at most, and often less.

12Brokers as Observers: Registration, Heartbeats, and Fencing

Brokers do not vote, but they are the reason the quorum exists. Here is how they stay in step.

Joining the cluster

When a broker starts, it first reads its local copy of the metadata log, if any, and then contacts the controllers through the controller listener. It sends a registration request that includes its id, its network endpoints, its rack, the features it supports, and the ids of the storage directories it owns. The active controller writes a record for this registration into the log. Once the record commits, the broker officially exists and receives a fresh broker epoch.

Staying alive: the heartbeat

After registering, a broker sends periodic heartbeats to the active controller. Each heartbeat carries the broker’s current metadata offset, meaning how far it has applied the log. The controller uses this to decide when the broker is caught up. Only a caught-up broker is allowed to move from the fenced state into the active state, where it can lead partitions.

flowchart LR
    A["Starting"] --> B["Registered and fenced"]
    B -->|"caught up with metadata log"| C["Active"]
    C -->|"heartbeat lease expires"| B
    C -->|"controlled shutdown requested"| D["Shutting down"]
    D -->|"partition leadership moved away"| E["Stopped"]
    B -->|"never catches up"| B
        
FIG 6 — The lifecycle of a broker from the controller’s point of view

Fencing

If a broker misses its heartbeat for longer than the session timeout, which defaults to about nine seconds, the controller fences it. A fenced broker is removed from leadership and from every in-sync replica set. The controller writes records for these changes, and other brokers learn about them from the log. A fenced broker that is actually still running will notice that it can no longer reach the controller or has been told it is fenced, and it will stop acting as a leader.

Simple Analogy

A lifeguard must wave to the tower every few seconds. If the tower sees no wave for too long, it announces that the lifeguard is off duty and assigns another to cover that stretch of beach. If the first lifeguard finally returns, they must check in and get a fresh badge before guarding again.

How metadata reaches brokers

Brokers do not receive pushes. Each broker runs a component that fetches new records from the controller quorum, applies them to its own image, and hands changes to local subsystems, such as the replica manager, the group coordinator, and the authorizer. Because everything flows through the log, each broker’s state is always “the log up to offset X”, a clean, comparable fact.

If a broker has fallen very far behind, or is brand new with no local copy, it does not need to replay millions of old records. It asks for the latest snapshot and then continues from there. The next chapter explains snapshots in detail.

!
Common Mistake

Treating brokers as if they were voters. Adding more brokers does not add fault tolerance to the metadata system. Only the number of controller voters determines how many controller failures the cluster can survive.

13Snapshots and Log Compaction

A log that only grows would eventually swallow the disk and make new nodes wait forever. Snapshots solve both problems.

The problem with an endless log

Every change ever made to the cluster is in the metadata log. After months or years there may be millions of records, many of them describing things that no longer matter, such as a topic that was created and deleted, or a leader that changed a hundred times. Replaying all of that on every restart would be wasteful.

What a snapshot is

A snapshot is a single file that captures the complete cluster state as it was at a particular offset and epoch. Think of it as the “current picture”, rather than the “history of every change”. Once a snapshot exists, all log entries before its offset are no longer needed to rebuild the state, and can be deleted to free space.

Simple Analogy

A bank could keep every transaction since the day it opened. To answer “what is my balance?”, it does not add up fifty years of receipts each time. It uses the end-of-month statement, and only adds the transactions after that. The snapshot is the statement, and the log after it is the recent receipts.

When snapshots are taken

Each node, leader or follower, takes its own snapshots from its own committed state. Two settings guide the timing: one based on how many bytes of new records have accumulated since the last snapshot, and one based on elapsed time. Whichever condition is met first triggers a new snapshot. Because the snapshot is produced from committed data only, it is always safe.

Bytes
Snapshot after enough new record data piles up
Time
Snapshot after a maximum interval passes
Committed
Only committed state is ever captured

Serving snapshots to slow or new nodes

Suppose a controller or broker asks for records that the leader has already deleted because a snapshot replaced them. The leader responds with the identity of its latest snapshot, telling the follower, in effect, “those entries are gone, here is the snapshot that includes them”. The follower then sends fetch-snapshot requests to download the snapshot in pieces, loads it, and resumes normal fetching right after the snapshot’s offset.

sequenceDiagram
    participant N as New or lagging node
    participant L as Leader
    N->>L: Fetch from offset 10
    L-->>N: Offset 10 is gone, use snapshot at offset 500000, epoch 7
    N->>L: Fetch snapshot piece 1
    L-->>N: Snapshot bytes
    N->>L: Fetch snapshot piece 2
    L-->>N: Snapshot bytes
    Note over N: Snapshot loaded, state rebuilt
    N->>L: Fetch from offset 500001
    L-->>N: Newest records
        
FIG 7 — A lagging node catching up by downloading a snapshot

Where it is stored

Snapshots live in the same directory as the metadata log, as files named after the offset and epoch they represent. Older snapshots are cleaned up once newer ones exist and are safely in place. The size of a snapshot is proportional to the amount of current cluster state, not to the amount of history, so it grows with the number of partitions, topics, configurations, and access rules, but not with age.

i
Why this helps brokers too

A broker that restarts loads its last snapshot and replays only the small tail of the log. Startup after a rolling restart is quick, even in a cluster with a very large number of partitions.

14Quorum Sizing and Failure Tolerance

How many controllers should you run, and what exactly happens when some of them fail?

The rule of thumb

Most production clusters run three dedicated controllers. Clusters that must survive two simultaneous controller failures, or that plan maintenance while staying resilient, use five. More than five is rarely useful, because each additional voter adds coordination work and slightly slows commits while only marginally improving availability.

Advantages of 3 voters

  • Survives one failure or one planned restart at a time
  • Lowest coordination cost for real fault tolerance
  • Simple to reason about and to place across three zones
  • Fewer machines to secure and monitor

Trade-offs of 3 voters

  • During a planned restart, a second failure halts metadata changes
  • Losing two nodes means loss of majority
  • Little room for combined maintenance and failure

Advantages of 5 voters

  • Survives two failures, including a restart plus a crash
  • Better fit for spreading across five failure domains
  • More headroom during upgrades and hardware replacements

Trade-offs of 5 voters

  • Each commit needs three acknowledgements instead of two
  • More hardware, more network paths, more monitoring
  • Diminishing returns beyond this point

Placing voters across failure domains

Fault tolerance is only real if failures are independent. Three controllers on three virtual machines that share one physical host fail together. A good design places each voter in a different failure domain, such as separate racks or availability zones. With three zones, one voter per zone tolerates the loss of an entire zone. With only two zones, the zone holding the majority becomes a single point of failure for metadata writes, so two-zone deployments need careful thought.

flowchart TD
    subgraph ZA["Zone A"]
        CA["Controller 1"]
        BA["Brokers 1 and 2"]
    end
    subgraph ZB["Zone B"]
        CB["Controller 2"]
        BB["Brokers 3 and 4"]
    end
    subgraph ZC["Zone C"]
        CC["Controller 3"]
        BC["Brokers 5 and 6"]
    end
    CA --- CB
    CB --- CC
    CA --- CC
    BA --> CA
    BB --> CB
    BC --> CC
        
FIG 8 — One controller per zone so any single zone can be lost

Latency between voters

Every commit needs acknowledgements from a majority. If voters are in far apart locations, each commit waits for the round trip to the nearest majority. Metadata changes are infrequent compared to data traffic, so moderate latency between zones in one region is fine. Stretching a quorum across continents is a different story: elections and commits become slow, and timeouts may need careful tuning. For multi-region resilience, most teams run independent clusters and replicate data between them rather than one giant quorum.

SituationEffect on metadataEffect on existing data traffic
One of three controllers downFully working, majority intactNone
Two of three controllers downWrites halt, no majorityExisting leaders keep serving, no leader changes possible
Active controller downShort pause, then new leader electedVery brief or no visible effect
All controllers downComplete metadata outageBrokers continue with last known state until something needs a change
Network split, majority on one sideMajority side keeps working, minority side stallsDepends on which side brokers are on

15Failure Scenarios and Their Impact on the Data Plane

Understanding what breaks, and what keeps running, is what makes a good on-call engineer.

The two planes

Kafka has a control plane, which is the controller quorum and its decisions, and a data plane, which is the brokers moving message data. They are loosely coupled. A healthy data plane can run for a while without the control plane, because leaders and replica lists already exist. But many normal events require a control plane decision, so the data plane cannot run forever without it.

Simple Analogy

Trains keep running on their current tracks even if the central dispatch office loses power. But if any train breaks down, or a track needs switching, nothing can be rerouted until the dispatch office is back.

What still works when the quorum has no majority

Producing and consuming on healthy partitions

Brokers keep their last known leader assignments, so produce and fetch requests for partitions whose leaders and followers are healthy continue as normal.

Existing consumer group activity

Group coordination is handled by brokers using their own state, so already running consumers largely continue, although rebalances involving metadata changes may struggle.

What stops working

Topic creation, deletion, and reconfiguration

Every one of these needs a committed metadata record. Requests wait and then fail with timeouts.

Leader election for partitions

If a broker fails during the outage, its partitions have no way to obtain a new leader, so they become unavailable until the quorum returns.

In-sync replica changes

A partition leader that loses a follower must ask the controller to shrink the in-sync replica list. Without the controller, the list stays unchanged. With strict acknowledgement settings, producers may then stall, because the missing follower still counts as required.

New broker registration and fencing

New or restarted brokers cannot join, and dead brokers cannot be fenced.

flowchart TD
    A["Quorum loses majority"] --> B["Metadata writes stop"]
    B --> C["Healthy partitions keep serving clients"]
    B --> D["Topic changes fail"]
    B --> E["Broker failure cannot be handled"]
    E --> F["Affected partitions become unavailable"]
    B --> G["ISR changes cannot be committed"]
    G --> H["Strict acks may stall producers"]
    A --> I["Majority restored"]
    I --> J["New leader elected, backlog processed"]
    J --> K["Normal operation resumes"]
        
FIG 9 — The ripple effects of losing controller majority

Recovery when a majority is lost for good

If two of three controllers are permanently destroyed, restarting the survivors will not help, because a majority is impossible without them. The safe recovery approach is to bring back replacement voters with a copy of the surviving node’s metadata, following the documented procedure for your Kafka version, or to restore from a known-good backup of the metadata directory. This is why keeping copies of the controller data directory, and rehearsing the procedure, is a valuable insurance policy.

!
Do not improvise

Forcing a single surviving node to declare itself leader, or editing quorum files by hand, can throw away committed decisions. Always use the supported recovery steps and, if you can, rehearse them in a test environment before you ever need them for real.

16Dynamic Quorum and Pre-Vote: Newer Improvements

Early KRaft had a fixed list of voters. Newer versions make the group flexible and more stable.

The limit of a static quorum

In the first design, the voter list was written into every node’s configuration. To replace a failed machine or to grow from three to five voters, an operator had to edit configs and restart nodes in a delicate order. A mistake could break the majority arithmetic.

Dynamic quorum reconfiguration

Newer Kafka releases allow controllers to be added or removed while the cluster runs. The current voter set is itself stored in the metadata log as special control records, so the quorum agrees on its own membership using the very same Raft rules. Nodes use a bootstrap server list only to discover the current quorum, and each voter has a unique directory identifier in addition to its node id, so a machine that lost its disk cannot be mistaken for its previous self.

1

New node starts as an observer

It is formatted, started with the controller role, and begins fetching the log like any other follower without a vote.

2

It catches up

Using fetches and, if needed, a snapshot, it reaches the leader’s position, so it will not slow down commits once it becomes a voter.

3

An operator requests the add

The request goes to the leader, which writes a new voter-set record into the log and, once it commits, the new node counts as a voter.

4

Removal works in the mirror image

A voter is taken out by committing a record with the smaller voter set. If the leader itself is removed, it steps down after the change commits.

i
One at a time

Kafka changes the voter set one member per step. Because two consecutive sets that differ by only one node always have overlapping majorities, no moment exists where two independent leaders could be elected. Changing many at once would need extra protocol machinery, so operators simply repeat the single step.

sequenceDiagram
    participant Op as Operator
    participant L as Leader
    participant V as Existing voters
    participant N as New controller
    N->>L: Fetch log as observer
    L-->>N: Records or snapshot
    Op->>L: Add voter request for N
    L->>L: Append voter set record
    V->>L: Fetch, store the record
    Note over L: Majority stored, record committed
    L-->>Op: Voter added
    Note over N: N now counts toward majorities
        
FIG 10 — Adding a controller to a running quorum

Pre-vote: avoiding disruptive elections

A subtle weakness in basic Raft is that a node cut off from the network keeps timing out and raising its epoch. When it reconnects, its high epoch forces the healthy leader to step down, causing a needless election even though the cluster was fine. The remedy, available in newer releases, is a pre-vote phase: before increasing its epoch, a would-be candidate first asks the others “would you vote for me if I ran?”. Voters that still hear from a healthy leader say no, so the isolated node never raises the epoch and never disturbs the cluster.

Simple Analogy

Before calling for a class election, a student first quietly asks classmates whether they even want a new captain. If everyone is happy with the current one, the student simply sits down, and nobody’s lesson is interrupted.

17Deployment Topologies and Moving from ZooKeeper

Where the controllers run, and how a cluster travels from the old world to the new one.

Isolated versus combined deployment

QuestionIsolated controllersCombined nodes
Resource contentionNone, controllers have their own machinesHeavy broker traffic can starve the controller role
Failure couplingBroker crashes do not touch the quorumA broker crash also removes a voter
CostExtra small machinesFewer machines
Upgrade and restart planningIndependent schedulesRestarting a node affects both roles
Recommended forProductionDevelopment and small experiments

Controller machines are usually modest. They need reliable low-latency disks, stable networking, and enough memory to hold the metadata image, but not the large disks and heavy network of a data broker.

Migrating a ZooKeeper cluster

Existing ZooKeeper-based clusters can move to KRaft without downtime through a staged migration. The idea is to add a KRaft quorum beside the running cluster, let it take over the role of controller while both worlds hold the same metadata, and then finish by removing ZooKeeper.

1

Prepare

Upgrade the cluster to a release that supports migration, and make sure it uses a compatible metadata version and healthy replication.

2

Deploy the controller quorum

Start new dedicated controllers in migration mode, configured to connect to ZooKeeper so they can copy its metadata into the new log.

3

Roll the brokers

Restart brokers one at a time so they register with the new controllers instead of the old controller.

4

Dual-write phase

The new active controller writes metadata to both its own log and ZooKeeper, keeping a way back open while everything is verified.

5

Finalize

Once satisfied, take the cluster fully into KRaft mode. After this step, going back is no longer possible, and ZooKeeper can be shut down.

!
One-way door

The last step of the migration cannot be undone. Take backups, rehearse in staging, and give the dual-write period enough time to reveal any surprises before finalizing.

18Security of the Controller Quorum

The controllers hold the keys to the entire cluster, so they deserve the tightest protection.

Why controllers are sensitive

Whoever can write to the metadata log can create topics, change access rules, alter configurations, and reassign partitions. A stolen or forged controller identity would give an attacker full control. Access to the metadata log files also reveals the structure of the entire cluster, including access rules and configuration values.

Layer 1

Network isolation

Place the controller listener on a private network. Only brokers and other controllers should be able to reach it, and clients never should.

Layer 2

Encryption in transit

Use TLS on the controller listener so replication and registration traffic cannot be read or altered on the wire.

Layer 3

Mutual authentication

Require controllers and brokers to prove who they are, through mutual TLS or SASL, so a rogue machine cannot pose as a voter.

Layer 4

Authorization

Give the controller and broker principals only the cluster-level permissions they need. Administrative actions need authorized principals too.

Layer 5

Disk protection

Restrict operating system access to the metadata directory and encrypt volumes, since the log and quorum state hold sensitive information.

Layer 6

Audit and change control

Log administrative actions, and require review for quorum membership changes and recovery procedures.

i
One security model

A major benefit of dropping ZooKeeper is that there is now a single authentication and authorization story for the whole platform. Fewer systems mean fewer places to misconfigure and fewer credentials to rotate.

19Monitoring, Logging, and Metrics

A quorum is quiet when it is healthy, so you need the right signals to notice trouble early.

Questions your dashboards should answer

Good monitoring for the controller quorum answers a handful of simple questions. Is there exactly one active controller? Do all voters agree on who the leader is and on the current epoch? Is every voter keeping up with the leader? Are elections rare? Are brokers applying the latest metadata? Are disks fast enough?

SignalWhat it tells youHealthy pattern
Active controller countWhether one node is currently in chargeExactly one across the cluster, never zero for long
Current epochHow many elections have happenedStable for weeks, small steps only after planned restarts
Current leader and current voteWhether voters agree on the leaderEvery voter reports the same leader
High watermark and log end offsetProgress of commits and of local writesHigh watermark closely trails the log end
Voter lagHow far each standby is behind the leaderSmall and steady, never growing
Election latencyHow long a leader change takesA few seconds at most and rare
Commit latencyTime for a record to reach a majorityLow and steady, spikes suggest slow disks or network
Last applied record lag on brokersHow stale each broker’s view isNear zero in normal operation
Metadata load and apply errorsFailures while processing recordsAlways zero
Snapshot age and sizeWhether compaction is workingRecent snapshots with stable size

Tools that ship with Kafka

Tool

Metadata quorum tool

Describes the quorum: the leader, the epoch, the high watermark, each voter’s position and lag, and the list of observers.

Tool

Metadata shell

Lets you browse a snapshot or the live metadata like a file system, handy for checking what the cluster believes about a topic or a broker.

Tool

Log dump tool

Reads records straight out of the metadata log or snapshot files, useful in investigations and post-mortems.

Tool

Storage tool

Formats a node’s storage with the cluster identity before first start and helps with bootstrapping the quorum.

Logs worth watching

Controller logs record every state change of the Raft manager: becoming candidate, granting a vote, learning about a new leader, resigning, and truncating a diverging log. Repeated messages about vote requests, timeouts to a particular peer, or a node repeatedly becoming a candidate are early warnings of network problems or overloaded machines. Messages about fetch timeouts from brokers point to slow links between brokers and controllers.

!
Alert on trends, not blips

A single election after a planned restart is normal. An election every few minutes is a fire. Set alerts on the rate of epoch changes and on prolonged periods without a leader, rather than on any single event.

flowchart TD
    A["Alert: no active controller"] --> B{"Do most voters respond?"}
    B -->|"No"| C["Check network, machines, disks on voters"]
    B -->|"Yes"| D{"Do voters agree on epoch?"}
    D -->|"No"| E["Look for partitioned or isolated voter, check timeouts and firewall rules"]
    D -->|"Yes"| F["Check election timing and pauses on the candidate"]
    C --> G["Restore majority, watch the election complete"]
    E --> G
    F --> G
        
FIG 11 — A simple triage path when the quorum has no leader

20Design Patterns and Anti-patterns

Some choices repeatedly prove wise, and some repeatedly cause outages.

Patterns worth copying

Dedicated small controller tier

Run three or five controller-only nodes on their own machines, sized for reliability rather than throughput. This isolates the brain from the heavy work of the brokers.

One voter per failure domain

Spread voters across zones or racks so no single event removes a majority. Match the count of voters to the count of independent domains where possible.

Automated, tested recovery runbooks

Write down and rehearse the steps for replacing a lost voter, restoring from backup, and finalizing upgrades. Rehearsed procedures turn panic into routine.

Backups of the metadata directory

Regularly copy snapshots and the quorum files from a healthy controller to safe storage, so a catastrophic loss of majority is survivable.

Anti-patterns to avoid

ANTI-PATTERN-02 Avoid
Problem

A production cluster runs combined-mode nodes, with every broker also serving as a voter, to save on hardware.

Why It’s Harmful

A busy broker can starve its controller role of CPU, memory, or disk, causing slow fetches and false timeouts. Rolling a broker restart also removes a voter, and restarting two at once can remove the majority. Data problems and metadata problems now share a single failure path.

Correct Approach

Use dedicated controller nodes for anything beyond development and small tests, and keep the controller tier separate from the broker tier.

ANTI-PATTERN-03 Avoid
Problem

A team deploys four or six controllers, believing that more voters always means more safety.

Why It’s Harmful

An even number of voters raises the majority requirement without raising the failure tolerance. Four voters tolerate one failure, just like three, but need three acknowledgements for every commit. A network split into two equal halves leaves neither side with a majority at all.

Correct Approach

Choose an odd number: three for most clusters, five for those that need to survive two failures.

ANTI-PATTERN-04 Avoid
Problem

All controllers are placed in the same rack, host group, or availability zone, and use the same power and network path.

Why It’s Harmful

The quorum looks redundant on paper, but a single shared fault removes every voter together, which is exactly the one event that quorum was supposed to survive.

Correct Approach

Use separate failure domains for each voter and verify that they do not share hidden dependencies such as the same storage array or the same switch.

ANTI-PATTERN-05 Avoid
Problem

Controllers are placed on slow or shared network storage, or on machines with bursty CPU credits, to save cost.

Why It’s Harmful

Every commit depends on durable writes to a majority. Slow or unpredictable disks cause slow commits, long stalls, and missed timeouts, which in turn trigger needless elections.

Correct Approach

Give voters fast local or well-provisioned disks and steady compute, even if the machines are small.

21Best Practices and Common Mistakes

A checklist of habits that keep controller quorums calm, and the errors that disturb them.

Best practices

  • Run three dedicated controllers, or five for higher resilience
  • Spread voters across independent failure domains
  • Keep the controller listener private, encrypted, and authenticated
  • Restart one controller at a time and wait until it has caught up before the next
  • Watch epoch changes, voter lag, and commit latency
  • Test recovery and backup restoration on a schedule

Common mistakes

  • Restarting two voters at once in a three-voter quorum
  • Using inconsistent voter lists or cluster ids across nodes
  • Copying or deleting quorum state files by hand
  • Forgetting that brokers do not add fault tolerance to the quorum
  • Treating a metadata outage as harmless because traffic still flows
  • Finalizing a migration without a rehearsal or a backup

A safe rolling restart, explained

1

Restart standbys first

Begin with followers, since stopping a follower does not cause an election.

2

Wait for the node to catch up

Confirm through the quorum tool that the restarted voter’s lag is close to zero before moving on.

3

Restart the leader last

A graceful stop of the active controller triggers a quick planned handover instead of a timeout-based election.

4

Verify

Check that all voters agree on the leader and epoch, and that brokers are applying the newest records.

Tuning timeouts carefully

The defaults for election and fetch timeouts suit most networks in a single region. Raising them makes elections rarer but leaves the cluster leaderless longer during a real failure. Lowering them speeds up failover but risks false elections during a garbage collection pause or a brief network stall. Change them only with measurements in hand, and keep the relationship sensible: the fetch timeout must comfortably exceed the time it takes a healthy voter to make and answer a fetch, including jitter.

i
Pause awareness

Long pauses in the Java virtual machine look exactly like a dead node to the others. Give controllers a comfortable heap, choose a modern collector, and watch pause times, because a single long pause can cost you an election.

22Inside the Storage Layout: What Sits on a Controller’s Disk

Opening the metadata directory of a controller shows how the ideas of this tutorial become real files.

Three kinds of files

A controller’s metadata directory holds three families of files that work together. The first is a set of log segments, which contain the metadata records in order. The second is a set of snapshot files, each one a full picture of the cluster state at a given offset and epoch. The third is the tiny quorum state file, which remembers the node’s voting history. Together, they are everything the node needs to rejoin the cluster after a restart.

File family 1

Log segments

Append-only files, split by size and age like ordinary Kafka log segments, each with companion index files so a specific offset can be found quickly.

File family 2

Snapshots

Files with a checkpoint-style name that encodes the end offset and epoch they represent. They are written to a temporary name first and renamed only when complete, so a half-written snapshot is never trusted.

File family 3

Quorum state

A small file recording the current epoch, the known leader, and the candidate this node voted for. It is updated carefully so a crash cannot leave it half written.

File family 4

Metadata properties

A short file that holds the cluster id, the node id, and the directory identifier, stamped when the storage was formatted, so the node can refuse to start with the wrong identity.

Simple Analogy

Imagine a committee secretary’s desk. There is a bound notebook of official minutes, which is the log. There is a yearly summary booklet that lets anyone skip the old pages, which is the snapshot. And there is a small sticky note that says “we are in the seventh term, and I promised my vote to Ms. Rao”, which is the quorum state. Lose the sticky note and the secretary might promise the same vote twice.

Why the quorum state file must be trusted

Raft’s safety depends on a simple promise: a voter grants at most one vote per epoch. If a voter could forget its vote after a crash, it might vote for one candidate, restart, and then vote for another in the same epoch. Two candidates could each collect a majority, and two leaders would share an epoch. That is why the quorum state is written to disk before the vote is announced, and why manual edits or restores of old copies of this file are so dangerous.

!
Backup timing

Restoring an old copy of a controller’s directory onto a running quorum member can make the node forget votes it already granted. Backups are best used for full-cluster disaster recovery, following the documented procedure, and not for casually rewinding a single node.

Durability and the meaning of “stored”

When this tutorial says that a follower “has stored” a record, it means the record was written in a way that survives a crash of that machine. The majority argument only holds if the records genuinely exist on stable storage when the acknowledgement is sent. If a node acknowledged data that lived only in memory, then a simultaneous power loss on several nodes could erase a record that was already announced as committed. This is why the choice of disks, the flushing behavior of the file system, and the reliability of the underlying hardware matter far more for controllers than raw disk size.

Good disk choices

  • Local solid state drives with steady write latency
  • Well-provisioned network volumes with guaranteed performance
  • Dedicated volume for the metadata directory, separate from noisy neighbors
  • Alerts on free space and on write latency

Risky disk choices

  • Shared volumes whose performance depends on other tenants
  • Burstable volumes that throttle after a credit balance runs out
  • Tiny volumes that can fill up during a burst of changes

How the log shrinks

Log segments are retained until a snapshot covers them and until every voter and the known observers no longer need them. The leader keeps enough of the log to serve normally lagging followers with plain fetches, and relies on snapshots for anyone who has fallen further behind. Deleting whole segments is a cheap file operation, and it happens in the background without blocking the write path.

flowchart LR
    A["Records arrive in log segments"] --> B["High watermark advances"]
    B --> C["State machine applies committed records"]
    C --> D{"Enough bytes or time since last snapshot?"}
    D -->|"Yes"| E["Write snapshot to temporary file"]
    E --> F["Rename to final snapshot name"]
    F --> G["Old segments covered by snapshot become deletable"]
    D -->|"No"| A
        
FIG 12 — How the log, high watermark, and snapshots cooperate to keep the disk tidy

23Capacity Planning, Timeouts, and Performance Tuning

A controller is small, but it is not free. This chapter shows how to size it and what to watch as the cluster grows.

What actually grows with the cluster

The demands on the controller tier do not grow with the amount of message traffic. They grow with the amount of metadata. More partitions mean a bigger in-memory image, bigger snapshots, and more records to replay. More brokers mean more heartbeats to process. More topics, more access rules, and more configuration overrides all add entries. A cluster that moves enormous amounts of data across a modest number of partitions barely troubles its controllers, while a cluster with a very high partition count and frequent topic churn keeps them busy.

Partitions
Main driver of image size and snapshot size
Brokers
Main driver of heartbeat and registration load
Churn
Frequent topic and config changes raise write rate

Sizing checklist

1

Memory

Each controller keeps the entire cluster image in memory, plus space for uncommitted state and for snapshots being written. Size the heap with comfortable headroom above the image size, and revisit it whenever the partition count doubles.

2

Disk

Reserve enough space for several snapshots and for the log that accumulates between snapshots. Prioritize write latency over capacity.

3

CPU

The single-threaded event loop benefits from fast cores rather than many cores. A few strong cores are usually better than many weak ones.

4

Network

Reliability and low jitter matter more than bandwidth. Metadata traffic is light, but fetch timeouts are sensitive to network hiccups.

5

Headroom for failure

Plan so that after losing one voter, the remaining voters can still handle the whole load, including the catch-up traffic when a replacement joins and downloads a snapshot.

The timeout family

Several timeouts govern the behavior of the quorum, and it helps to see them as a small family that must be consistent with each other.

TimeoutWhat it controlsIf set too lowIf set too high
Fetch timeoutHow long a follower waits for the leader before starting an electionFalse elections during brief pausesSlow detection of a dead leader
Election timeoutHow long a candidate waits for votes before trying againFrequent split votesSlow recovery from a failed election round
Election backoffThe random delay before a follower becomes a candidateMore split votes because candidates start togetherLonger leaderless period
Request timeoutHow long to wait for a reply to a single requestRequests abandoned during ordinary slownessSlow reaction to a stuck peer
Broker session timeoutHow long the controller waits for a broker heartbeat before fencingHealthy brokers get fenced by hiccupsDead brokers keep leadership too long
i
Keep the ratios healthy

A useful mental rule is that detection of a failure should be several times longer than a normal round trip, and an election attempt should be longer than a single vote exchange. Changing one timeout in isolation can break these ratios and turn a calm cluster into a jittery one.

Capacity signals that say “grow now”

Commit latency drifting upward

If the time for records to reach a majority keeps creeping up over weeks, the disks or the network of at least one voter are struggling, or the change rate is rising faster than expected.

Snapshot duration or size climbing quickly

A fast-growing snapshot means the number of partitions or other entities is growing. Make sure memory and disk headroom keep pace before the growth becomes urgent.

Brokers lagging in applying metadata

When the last-applied position on brokers trails the leader for long periods, either the brokers or the controller path is overloaded, and rolling operations may become slower and riskier.

Rising heap usage and pause times

More time in garbage collection on a controller raises the chance of a missed fetch and a needless election. Add memory or reduce the metadata footprint before pauses grow long.

ANTI-PATTERN-06 Avoid
Problem

To make failover “instant”, an operator drastically lowers all election and fetch timeouts without measuring real network behavior.

Why It’s Harmful

Ordinary hiccups, such as a brief garbage collection pause or a busy network moment, now look like leader death. The cluster holds repeated elections, each briefly halting metadata writes, and the total downtime becomes worse than with the defaults.

Correct Approach

Measure the real distribution of round trips and pauses first. Change one setting at a time, keep sensible ratios, and confirm improvement in a staging environment before touching production.

“The best controller tuning is boring: a stable epoch, a steady lag, and a dashboard nobody needs to stare at.”

24Real-World and Industry Examples

The same ideas play out in very different kinds of organizations.

Large streaming platforms with very high partition counts

Companies that run clusters with hundreds of thousands of partitions were the main driver of KRaft. Faster controller failover, faster startup, and cheaper topic creation and deletion made huge topic counts practical.

Financial services and payment processors

Teams in this world value predictable recovery. A three-zone deployment with one voter per zone, strict network segmentation, mutual TLS, and rehearsed recovery drills maps neatly onto the audit and resilience requirements of regulated environments.

Ride-hailing and delivery marketplaces

These businesses create many short-lived topics and frequently scale brokers up and down with traffic. The faster registration and metadata propagation of the log-based design supports this elasticity better than push-based updates.

Managed cloud Kafka services

Providers who operate thousands of clusters benefit from removing a second system from every deployment. Fewer components per cluster means lower operating cost and fewer categories of incident.

Edge and small-footprint installations

Retail stores, factories, and test labs often run one small cluster using combined nodes. The simplicity of a single process type is a large win, provided the team accepts the reduced isolation.

Enterprises migrating older clusters

Many organizations with years of ZooKeeper-based Kafka use the staged migration path, first in a test environment, then in lower-risk clusters, and finally in the crown-jewel systems, treating each step as a rehearsal for the next.

ConcernZooKeeper eraKRaft era
Systems to operateKafka plus a separate ZooKeeper ensembleKafka only
Metadata storageZooKeeper data tree plus copies in memoryOne replicated log with snapshots
How brokers learn changesPushed by the controller through RPC callsPulled from the log by offset
Controller failoverLoad all state, then continueWarm standby already holds the state
Partition scaleLimited by ZooKeeper and push overheadMuch higher ceiling
Security modelTwo separate modelsOne model

25Frequently Asked Questions

Q1Is the active controller the same thing as the Raft leader?

Yes. Whichever voter currently wins the election is the Raft leader and also plays the role of active controller. The other voters are standby controllers that follow the log.

Q2Do brokers take part in voting?

No. Brokers are observers. They read the metadata log but never vote and never count toward a majority. Only nodes listed as voters in the quorum can vote.

Q3Why is the metadata log a single partition?

Ordering is essential. Every decision must be applied in exactly the same order everywhere, and a single ordered log gives that guarantee without any extra coordination between partitions.

Q4What happens to producers and consumers if the controller leader changes?

Usually nothing noticeable. Clients talk to brokers for data, and brokers keep serving existing partitions. Only actions that need a metadata change, such as creating a topic, may wait briefly until the new leader is elected.

Q5Can I run two controllers to save money?

You can, but it is worse than one. A majority of two is two, so losing either node stops all metadata writes. Two voters double the chance of an outage without adding any tolerance.

Q6Why do we need an odd number of voters?

Because an even number raises the majority bar without raising the count of failures you can survive, and a perfectly even network split leaves neither side with a majority.

Q7What is the difference between an epoch and an offset?

An offset is a position in the log, counting records. An epoch is a counter for leader reigns, increasing with each new election. Together, an epoch and offset pair identifies a precise point in the history of the log.

Q8How is Kafka’s Raft different from etcd or Consul style Raft?

Those systems follow the paper closely, with the leader pushing entries out. Kafka’s version has followers pull entries using fetch requests, reuses Kafka’s own log storage, treats non-voting observers as a natural part of the design, and uses the word epoch instead of term.

Q9Does KRaft make Kafka slower or less safe than ZooKeeper?

Neither. Recovery and scaling of the control plane are considerably better, and the safety rules come from a well-studied consensus algorithm. The data path for producing and consuming is unchanged.

Q10What happens if a controller’s disk fills up?

The node will fail to append or snapshot, become an unreliable voter, and may crash. Give the metadata directory enough space and alert on free space early, since a lost voter reduces your safety margin.

Q11Can I add or remove controllers without downtime?

In newer releases, yes. The voter set is stored in the log and can be changed one member at a time while the cluster continues to work. Older static setups required config edits and restarts.

Q12Is it safe to keep using ZooKeeper mode?

Recent major releases no longer support it, so staying on it means staying on older versions that will stop receiving fixes. Planning a migration to KRaft is the sensible path.

Q13What is stored in the quorum state file?

The current epoch, the leader the node knows about, and whom the node voted for. Remembering these across restarts prevents a node from voting twice in one epoch, which is a crucial safety rule.

Q14Why can a partition stall if the controller is down but the leader broker is healthy?

If a follower falls behind or dies, the leader must ask the controller to remove it from the in-sync list. Without a working controller, the list cannot change, and strict acknowledgement settings keep waiting for the missing follower.

Q15Can a broker become a voter later?

A node has a fixed set of jobs chosen through its process roles. To make a machine a voter, run it with the controller role, let it catch up as an observer, and then add it to the voter set through the supported membership change in newer releases. A pure broker does not become a voter by itself.

Q16Why does a new leader not commit old entries immediately?

A fresh leader is careful about entries from earlier epochs that it holds but that were never confirmed as stored on a majority. It first appends a marker record in its own epoch, and only once a majority stores that marker does everything before it become committed. This small rule avoids a rare but real safety hazard described in the Raft research.

Q17How quickly do brokers learn about a change?

As soon as the change is committed and the next fetch from each broker arrives, which is normally a small fraction of a second in a healthy cluster. Brokers apply records strictly in order, so a broker never sees a later change before an earlier one.

Q18What does the controller do when a broker dies?

After the broker misses its heartbeats for longer than the session timeout, the controller fences it, removes it from in-sync replica lists, and elects new leaders for the partitions it led, choosing from the remaining in-sync replicas. All of these changes are written as records so every broker learns them.

Q19Is it a problem if one controller is far slower than the others?

A slow voter is not fatal, because only a majority is needed. But it reduces your safety margin, since the cluster then depends on the other voters staying healthy. Investigate a persistently slow voter quickly rather than living with it.

Q20Why does Kafka use the follower’s fetch traffic as a heartbeat?

Because followers already need to contact the leader constantly to get new data. Using those same requests to show that both sides are alive avoids extra message types and keeps the protocol small, which means fewer opportunities for bugs.

Q21What is the difference between a controller quorum and a consumer group coordinator?

The controller quorum manages cluster-wide facts, such as brokers, topics, and partition leaders. A group coordinator is a role run by a broker, which manages membership and offsets for one consumer group. They are separate systems that both rely on the metadata to know where things live.

Q22How can I check that my quorum is healthy today?

Ask the metadata quorum tool for the current status. Look for a single leader, the same epoch reported everywhere, a high watermark that advances, and a small, steady lag for every voter and every observer. Then compare with your monitoring dashboards for election counts and commit latency.

26Summary and Key Takeaways

Kafka replaced an outside coordination service with a small group of its own nodes that keep one shared, ordered record of every cluster decision. Those nodes, the controller quorum, agree on that record using a pull-based version of the Raft algorithm. One voter leads at a time, others copy its log by fetching, a majority must store each record before it counts as committed, and epochs make sure that an old leader can never override a new one. Brokers simply follow the log, snapshots keep it small, and newer releases let the group change its own membership and resist disruptive elections. Understanding these few ideas lets you size, secure, monitor, upgrade, and troubleshoot a Kafka cluster with confidence.

Key Takeaways

  • Metadata is a log — cluster state is the result of replaying an ordered stream of records stored in a single-partition metadata log, so “how current is this node” is just an offset.
  • Voters decide, observers follow — only controller voters take part in elections and majorities, while brokers are observers that fetch and apply committed records.
  • Majority is the safety net — every commit and every election needs more than half the voters, which is why odd sizes of three or five are the sensible choices.
  • Epochs fence the past — a higher epoch always wins, so stale leaders are rejected, unable to commit, and forced to truncate their uncommitted entries.
  • Pull-based replication is Kafka’s twist — fetch requests serve as data transfer, heartbeat, and acknowledgement, and the same mechanism repairs diverging logs and serves snapshots.
  • Control plane loss is not harmless — healthy partitions keep serving, but leader elections, topic changes, and in-sync replica updates all stop until a majority returns.
  • Operate it like the brain it is — use dedicated controllers across failure domains, secure the controller listener, watch epoch changes and voter lag, restart one voter at a time, and rehearse recovery.