Kafka KRaft
Kafka KRaft stands for Kafka Raft metadata mode. It is the way Apache Kafka manages its internal metadata by itself, without ZooKeeper. Since Kafka 4.0, KRaft is the only supported mode.
This guide explains why KRaft was introduced, how controllers and brokers work together, how the metadata quorum survives failures, and how to set up, inspect and monitor a KRaft cluster, with formatted configuration, command-line and Java examples. It builds on Kafka Architecture and Kafka Cluster.
1. What Is Kafka KRaft?
Every Kafka cluster needs metadata: information about brokers, topics, partitions, partition leaders, configurations and access rules. Earlier, Kafka stored this information in a separate system, Apache ZooKeeper. With KRaft, Kafka stores and manages all of it itself.
In simple words, KRaft removes ZooKeeper and makes Kafka a self-managed and simpler system: one product to install, configure, secure and monitor.
Before (ZooKeeper mode) Now (KRaft mode)
+------------------+ +--------------------------+
| ZooKeeper | <- metadata | Kafka controllers (Raft) | <- metadata
| ensemble | +--------------------------+
+------------------+ |
| v
+------------------+ +--------------------------+
| Kafka brokers | <- data | Kafka brokers | <- data
+------------------+ +--------------------------+
two systems one system
2. Why Was Kafka KRaft Introduced?
ZooKeeper worked well for many years, but it added extra complexity. Teams had to install, manage, monitor and secure two distributed systems instead of one. It also limited Kafka’s growth:
- Metadata had to be copied between ZooKeeper and the active controller, which was slow for clusters with many partitions.
- When the controller changed, the new one had to load all metadata from ZooKeeper before it could act, delaying recovery.
- ZooKeeper had its own configuration, security model and upgrade process.
Kafka KRaft was introduced to:
- Simplify Kafka’s architecture.
- Reduce operational overhead.
- Improve scalability to millions of partitions.
- Increase reliability and speed up recovery.
3. Key Features of Kafka KRaft
| Feature | Explanation |
|---|---|
| No ZooKeeper dependency | Kafka handles metadata management itself through its own controller nodes |
| Built-in metadata log | Topics, partitions, broker information and configurations are stored in an internal log called __cluster_metadata |
| Raft consensus algorithm | Controllers use Raft to elect a leader and keep metadata consistent across the quorum |
| Better scalability | Clusters can handle far more partitions, because metadata changes are small log appends rather than full reloads |
| Faster startup and recovery | Standby controllers already hold the metadata in memory, so a new active controller takes over almost instantly |
| Easier operations | One system to deploy, secure, monitor and upgrade, with one configuration style |
4. KRaft Version Timeline
| Kafka version | KRaft milestone |
|---|---|
| 2.8 (2021) | KRaft available as an early-access preview |
| 3.3 (2022) | KRaft declared production-ready for new clusters |
| 3.5 (2023) | ZooKeeper mode deprecated; migration from ZooKeeper available as a preview |
| 3.6 (2023) | ZooKeeper-to-KRaft migration production-ready |
| 3.9 (2024) | Last version supporting ZooKeeper; dynamic controller quorums added |
| 4.0 (2025) | ZooKeeper support removed; KRaft is the only mode |
5. Node Roles: Brokers and Controllers
In KRaft mode, every Kafka node has one or two roles, set with process.roles:
| process.roles | What the node does | Typical use |
|---|---|---|
broker | Stores topic data and serves producers and consumers | Most nodes in a production cluster |
controller | Stores the metadata log and takes part in the Raft quorum | 3 or 5 dedicated nodes in production |
broker,controller | Does both jobs in one process (combined mode) | Development, testing and very small clusters |
Among the controllers, one is the active controller (the Raft leader). It makes all metadata decisions, such as creating topics and choosing partition leaders. The other controllers are followers that keep an up-to-date copy of the metadata log, ready to take over.
6. How Kafka KRaft Works
In a KRaft-based cluster:
- Some Kafka nodes act as controllers.
- Controllers manage metadata using the Raft protocol.
- One controller becomes the leader (active controller); the others are followers.
- Brokers continuously fetch new metadata records from the active controller and keep a local copy.
All metadata changes, like creating a topic, go through the leader. The leader appends them to the metadata log, and a change is committed once a majority of controllers have stored it. Brokers then apply it.
Admin: create topic "orders"
|
v
+---------------------+ replicate +---------------------+
| Controller 1 | -------------> | Controller 2 |
| ACTIVE (leader) | -------------> | Controller 3 |
| __cluster_metadata | | (followers) |
+---------------------+ +---------------------+
|
| brokers fetch new metadata records
v
+----------+ +----------+ +----------+
| Broker 1 | | Broker 2 | | Broker 3 | <- create partitions, serve clients
+----------+ +----------+ +----------+
To keep the log from growing forever, controllers regularly write snapshots of the full metadata state. A new or restarted node loads the latest snapshot and then reads only the records after it.
7. Simple Example of Kafka KRaft
Imagine a small Kafka cluster with 3 nodes running in combined mode (each node is both a broker and a controller): Node 1, Node 2 and Node 3. In KRaft mode:
- Node 1 is elected as the controller leader.
- Node 2 and Node 3 are controller followers.
Now, when you create a new topic:
- The request goes to the controller leader (Node 1).
- The leader writes the metadata change to the Raft log.
- The change is replicated to Node 2 and Node 3.
- Once a majority has stored it, the change is committed, and the brokers create the topic’s partitions.
If Node 1 fails, Node 2 and Node 3 hold an election and one of them becomes the new controller leader within moments. No ZooKeeper is involved at any step. In production, the same process runs on dedicated controller nodes instead of combined nodes.
8. Controller Quorum and Fault Tolerance
Raft needs a majority of controllers to agree on every change. That decides how many controller failures a cluster can survive:
| Controllers | Majority needed | Controller failures tolerated | Recommendation |
|---|---|---|---|
| 1 | 1 | 0 | Development only |
| 3 | 2 | 1 | Common production choice |
| 4 | 3 | 1 | No benefit over 3; avoid even numbers |
| 5 | 3 | 2 | Large or very critical clusters |
If the majority is lost, the cluster cannot make metadata changes (such as creating topics or electing new partition leaders), although brokers can keep serving existing partitions for a while. Place controllers in different racks or availability zones so one failure cannot take out the majority.
9. Running a Single-Node KRaft Cluster
For learning, one node can play both roles. Kafka 4.x ships with a ready-made configuration in config/server.properties. The important settings are:
# This node is both a broker and a controller
process.roles=broker,controller
node.id=1
# The controller quorum (just this node)
controller.quorum.bootstrap.servers=localhost:9093
# Client listener on 9092, controller listener on 9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
advertised.listeners=PLAINTEXT://localhost:9092
controller.listener.names=CONTROLLER
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
log.dirs=/tmp/kraft-combined-logs
# 1. Generate a cluster ID
KAFKA_CLUSTER_ID="$(bin/kafka-storage.sh random-uuid)"
# 2. Format the log directory (first start only)
bin/kafka-storage.sh format --standalone -t "$KAFKA_CLUSTER_ID" -c config/server.properties
# 3. Start the node
bin/kafka-server-start.sh config/server.properties
Formatting writes the cluster ID and initial metadata into the log directory. Every node in a cluster must be formatted with the same cluster ID.
10. A Production-Style KRaft Cluster
In production, controllers and brokers usually run on separate machines. Below are example settings for three dedicated controllers and any number of brokers.
process.roles=controller
node.id=1
# All controller voters: id@host:port
controller.quorum.voters=1@controller-1:9093,2@controller-2:9093,3@controller-3:9093
listeners=CONTROLLER://controller-1:9093
controller.listener.names=CONTROLLER
log.dirs=/var/lib/kafka/metadata
process.roles=broker
node.id=4
# Where to find the controllers
controller.quorum.voters=1@controller-1:9093,2@controller-2:9093,3@controller-3:9093
controller.listener.names=CONTROLLER
listeners=PLAINTEXT://broker-4:9092
advertised.listeners=PLAINTEXT://broker-4.example.com:9092
inter.broker.listener.name=PLAINTEXT
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
log.dirs=/var/lib/kafka/data
Controllers 2 and 3 use the same file with their own node.id and host name. Every node.id in the cluster must be unique. Format each node with the shared cluster ID before its first start, and secure the listeners with TLS and authentication in real deployments (see Kafka Security).
Kafka 3.9 and later also support a dynamic quorum, configured with controller.quorum.bootstrap.servers, which lets you add or remove controllers without restarting the whole quorum.
11. Checking the Metadata Quorum
The kafka-metadata-quorum.sh tool shows the health of the controller quorum:
bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status
ClusterId: Mk3OEYBSD34fcwNTJENDM2Qk
LeaderId: 1
LeaderEpoch: 7
HighWatermark: 10432
MaxFollowerLag: 0
MaxFollowerLagTimeMs: 12
CurrentVoters: [1,2,3]
CurrentObservers: [4,5,6]
| Field | Meaning |
|---|---|
LeaderId | Node ID of the active controller |
LeaderEpoch | Increases every time a new leader is elected |
HighWatermark | Offset of the last committed metadata record |
MaxFollowerLag | How far the slowest controller is behind; should be close to 0 |
CurrentVoters | Controllers that vote in elections |
CurrentObservers | Brokers that follow the metadata log without voting |
Use describe --replication to see each node’s position in the metadata log. Monitor the quorum with JMX metrics as well, for example the active controller count, which should be exactly 1 across the cluster.
12. Reading Quorum Information in Java
The Java Admin client can read the same information, which is useful for health checks and dashboards:
package com.example.kafka;
import java.util.Properties;
import org.apache.kafka.clients.admin.Admin;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.QuorumInfo;
public class QuorumInfoReader {
public static void main(String[] args) throws Exception {
Properties props = new Properties();
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
try (Admin admin = Admin.create(props)) {
// Details of the Raft metadata quorum
QuorumInfo quorum = admin.describeMetadataQuorum().quorumInfo().get();
System.out.println("Active controller (leader) ID: " + quorum.leaderId());
System.out.println("Leader epoch: " + quorum.leaderEpoch());
for (QuorumInfo.ReplicaState voter : quorum.voters()) {
System.out.printf("Voter %d at metadata offset %d%n",
voter.replicaId(), voter.logEndOffset());
}
System.out.println("Observers (brokers): " + quorum.observers().size());
}
}
}
Active controller (leader) ID: 1
Leader epoch: 7
Voter 1 at metadata offset 10432
Voter 2 at metadata offset 10432
Voter 3 at metadata offset 10431
Observers (brokers): 3
Client applications such as producers, consumers and Spring Boot services need no changes for KRaft. They connect to brokers exactly as before; only the cluster’s internal metadata handling is different.
13. Kafka with ZooKeeper vs Kafka KRaft
| Aspect | Kafka with ZooKeeper | Kafka KRaft |
|---|---|---|
| Metadata storage | ZooKeeper | Kafka itself (__cluster_metadata log) |
| Architecture | Two systems | Single system |
| Operational complexity | High | Lower |
| Controller failover | New controller reloads all metadata from ZooKeeper | Standby controllers are already up to date |
| Scalability | Practical limit of around 200,000 partitions | Millions of partitions |
| Security setup | Kafka and ZooKeeper secured separately | One security model |
| Supported in Kafka 4.0+ | No | Yes |
14. Moving from ZooKeeper to KRaft
Existing ZooKeeper-based clusters must move to KRaft before upgrading to Kafka 4.0. Kafka 3.x provides an online migration that keeps the cluster running. At a high level:
- Upgrade the cluster to a recent 3.x version (3.9 is the recommended bridge release).
- Deploy a new KRaft controller quorum with migration enabled.
- Restart the brokers one at a time so they register with the new controllers; metadata is copied from ZooKeeper.
- Restart the brokers again in pure KRaft mode.
- Finalize the migration, remove ZooKeeper settings, and shut down ZooKeeper.
- Upgrade to Kafka 4.x.
Test the migration in a non-production environment first, and follow the official migration guide for your exact version, because finalizing the migration cannot be undone. See Kafka ZooKeeper for background on the old mode.
15. When Should You Use Kafka KRaft?
For any new cluster, KRaft is the only choice: Kafka 4.0 and later do not support ZooKeeper. KRaft is especially helpful if:
- You are setting up a new Kafka cluster.
- You want simpler deployment and maintenance.
- You plan to scale Kafka to many topics and partitions.
- You want to avoid managing and securing ZooKeeper.
- You want to upgrade to Kafka 4.x and later.
16. Best Practices
| Do | Avoid |
|---|---|
| Use 3 (or 5) dedicated controllers in production | Running an even number of controllers |
| Use combined mode only for development or tiny clusters | Mixing heavy broker traffic with controller duties in large clusters |
| Spread controllers across racks or availability zones | Placing all controllers in one location |
| Give controllers fast, reliable disks | Sharing controller disks with noisy workloads |
| Format every node with the same cluster ID | Formatting nodes separately with different IDs |
| Secure the controller listener with TLS | Leaving controller traffic unencrypted |
| Monitor the leader, follower lag and active controller count | Noticing quorum problems only when topics cannot be created |
17. Summary
Kafka KRaft is a major improvement in Kafka’s architecture. It removes ZooKeeper and lets Kafka manage its own metadata using the Raft consensus algorithm: a small quorum of controllers stores the metadata log, one active controller makes decisions, and brokers follow the log to stay up to date. KRaft became production-ready in Kafka 3.3 and is the only mode from Kafka 4.0 onward.
With KRaft, Kafka becomes:
- Simpler to manage.
- More scalable.
- Faster to recover.
- Ready for the future.
If you are learning or working with modern Kafka, understanding KRaft is essential, because it is how every current Kafka cluster runs.
18. Frequently Asked Questions (FAQ)
Q1. What is Kafka KRaft in simple words?
KRaft is Kafka’s built-in way of managing cluster metadata without ZooKeeper. A small group of Kafka controller nodes stores the metadata in a replicated log and uses the Raft algorithm to agree on every change.
Q2. What does KRaft stand for?
Kafka Raft. It combines Kafka with the Raft consensus algorithm, which lets a group of servers agree on the same ordered list of changes even when some of them fail.
Q3. Is ZooKeeper still needed for Kafka?
No. Kafka 4.0 removed ZooKeeper support completely. All new clusters use KRaft, and older ZooKeeper-based clusters must migrate to KRaft on a 3.x version before upgrading to 4.0.
Q4. Since which version is KRaft production-ready?
KRaft was declared production-ready for new clusters in Kafka 3.3 (2022). Migration from ZooKeeper became production-ready in 3.6, and Kafka 4.0 (2025) made KRaft the only mode.
Q5. What is a KRaft controller?
A Kafka node with process.roles=controller that stores the metadata log and votes in the Raft quorum. One controller is the active controller that makes all metadata decisions; the others stand by with an up-to-date copy.
Q6. What is the difference between a broker and a controller in KRaft?
A broker stores topic data and serves producers and consumers. A controller stores and manages cluster metadata. A node can be either one, or both at once in combined mode.
Q7. What is combined mode?
Combined mode means one process is both a broker and a controller (process.roles=broker,controller). It is convenient for development and very small clusters. In production, dedicated controllers are recommended so heavy broker load cannot slow down metadata handling.
Q8. How many controllers should a KRaft cluster have?
Usually 3, which tolerates one controller failure. Use 5 to tolerate two failures in large or critical clusters. Avoid even numbers, because they add cost without improving fault tolerance.
Q9. What happens if the active controller fails?
The remaining controllers notice the missing heartbeats and elect a new leader, usually within seconds. Because followers already hold the latest metadata, the new active controller can start working immediately. Clients are not affected.
Q10. What happens if most controllers fail?
Without a majority, the quorum cannot commit metadata changes. You cannot create topics or move partition leaders until enough controllers return, although brokers can keep serving existing partitions for a while. This is why controllers should be spread across failure zones.
Q11. Where does KRaft store metadata?
In an internal Kafka log called __cluster_metadata, stored in each controller’s log directory and replicated through Raft. Controllers also write regular snapshots so nodes can load the current state quickly.
Q12. How do brokers get metadata in KRaft?
Brokers continuously fetch new records from the active controller’s metadata log and apply them to their local copy. They act as observers: they follow the log but do not vote in elections.
Q13. Why must I format storage before starting a KRaft node?
kafka-storage.sh format writes the cluster ID and initial metadata into the log directory. It makes sure every node belongs to the same cluster and prevents a node from accidentally joining the wrong one. Format each node once, with the same cluster ID.
Q14. How do I check the health of the KRaft quorum?
Run:
bin/kafka-metadata-quorum.sh --bootstrap-server localhost:9092 describe --status
It shows the leader, epoch, voters, observers and follower lag. In Java, use Admin.describeMetadataQuorum().
Q15. Do my producers and consumers need changes for KRaft?
No. Clients connect to brokers with bootstrap.servers exactly as before. KRaft only changes how the cluster manages metadata internally, so Java, Spring Boot and other client code stays the same.
Q16. What is controller.quorum.voters?
A setting that lists all controllers in the quorum as id@host:port, for example 1@c1:9093,2@c2:9093,3@c3:9093. Every broker and controller needs it to find the quorum. Kafka 3.9 and later also support controller.quorum.bootstrap.servers for dynamic quorums.
Q17. Can I add or remove controllers later?
Yes, with a dynamic quorum (Kafka 3.9 and later), using kafka-metadata-quorum.sh add-controller and remove-controller. Clusters set up with a static controller.quorum.voters list need a planned procedure to change the voter set.
Q18. How many partitions can a KRaft cluster support?
Far more than ZooKeeper-based clusters, which were practically limited to around 200,000 partitions. KRaft is designed to support millions of partitions, although broker hardware and good partition planning still matter.
Q19. How do I migrate from ZooKeeper to KRaft?
On Kafka 3.x (3.9 is recommended), deploy a KRaft controller quorum with migration enabled, roll the brokers so metadata is copied from ZooKeeper, roll them again in KRaft mode, and finalize the migration. Then remove ZooKeeper and upgrade to 4.x. Always test first and follow the official guide for your version.
Q20. Is KRaft faster than ZooKeeper mode?
For metadata operations, yes. Controller failover is much faster because standby controllers are already up to date, and creating or changing many partitions is quicker. Message throughput for producers and consumers is about the same, because data handling did not change.