Kafka

Deep Dive into KRaft Mode: Removing ZooKeeper and Designing the Metadata Plane

2026-04-19
NicheeLab Editorial Team

KRaft is the operating mode in which Kafka itself replicates metadata and forms a cluster without ZooKeeper. A controller quorum manages a metadata log via Raft consensus, and brokers subscribe to the resulting applied state.

This article walks through the rationale for removing ZooKeeper, the KRaft metadata design, the key properties, deployment and migration, and availability/security design — focusing on the points most likely to appear on the CCAAK exam.

KRaft Essentials and Exam Focus

KRaft brings Kafka's control plane in-house: a controller quorum agrees on and replicates the metadata log via Raft. ZooKeeper is no longer required, and cluster formation and metadata updates are completed entirely within the Kafka lifecycle.

In production, the key decisions are: dedicated controller nodes vs. co-located with brokers, sizing an odd-numbered quorum, and storage initialization (kafka-storage.sh). On the exam, property names, startup order, and KRaft-specific concepts (metadata log, snapshots) appear frequently.

  • ZooKeeper is not used at all in KRaft mode. Mixing the two is not supported.
  • The controller quorum uses an odd number of nodes (e.g., 3, 5) for majority consensus.
  • Use node.id; do not confuse it with the legacy broker.id setting.
  • controller.listener.names and listeners must be kept consistent.
  • Generate a cluster ID with kafka-storage.sh and format storage before starting.
TermMeaning in KRaftExam check points
Controller QuorumGroup of controllers that agree on and replicate metadataOdd-sized, voters config, leader concept
Metadata LogImmutable log of metadata changes (managed by controllers)Relationship between snapshots and replay
SnapshotCompacted point-in-time view of metadata stateFast bootstrap and recovery procedure
process.rolesAssigns broker / controller rolesWhether to co-locate or separate, and gotchas
node.idUnique node ID in KRaftDifference from the deprecated broker.id

KRaft logical architecture (high level)

metadata updates / subscriptionsClientsBrokers(data)Controller Quorum(Raft: leader & followers)KRaft logical architecture (high level)

Generating a cluster ID and initializing storage (mandatory steps)

# 1) Generate a cluster ID
$ KAFKA_HOME/bin/kafka-storage.sh random-uuid
hd9a2a0f-1b23-4c56-9d78-90efab12cd34

# 2) Format storage using server.properties
$ KAFKA_HOME/bin/kafka-storage.sh format -t hd9a2a0f-1b23-4c56-9d78-90efab12cd34 -c config/server.properties

Why ZooKeeper Is Gone: ZooKeeper vs. KRaft

In KRaft, the control plane and data plane converge inside Kafka. This eliminates the external ZooKeeper dependency and its operational burden (monitoring, upgrades, scaling), improving consistency and the predictability of metadata-change latency.

Compare them across metadata storage format, consensus algorithm, fault tolerance, and configuration/operational simplicity. It is important to align your mental model with the terminology in the official docs.

  • ZooKeeper: hierarchical tree + watchers. KRaft: log + snapshots.
  • Zab vs. Raft (implemented as Kafka Raft). Both rely on majority consensus, but their implementations and responsibility boundaries differ.
  • In KRaft, brokers subscribe to the applied state of the metadata log and update their state immediately.
AspectZooKeeper clusterKRaft (Controller Quorum)
Control planeExternal (ZooKeeper)In-house in Kafka (controllers)
Metadata formatZNode hierarchyImmutable log + snapshots
Consensus algorithmZabRaft (majority consensus)
Dependencies / operationsRequires designing and monitoring a separate clusterSimplified by single-cluster operation with Kafka
Key configzookeeper.connectprocess.roles, controller.quorum.voters etc.
NetworkRequires opening ZooKeeper portsCan be isolated via the CONTROLLER listener

Before / After conceptual diagram

BrokersBeforeZooKeeperBrokersAfterController Quorum(Raft)Before (ZooKeeper) / After (KRaft)

Config comparison example

# Old (ZooKeeper-based)
# zookeeper.connect=zk1:2181,zk2:2181,zk3:2181
# broker.id=1

# New (KRaft)
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@node1:9093,2@node2:9093,3@node3:9093
controller.listener.names=CONTROLLER
listeners=PLAINTEXT://node1:9092,CONTROLLER://node1:9093
inter.broker.listener.name=PLAINTEXT
metadata.log.dir=/var/lib/kafka/metadata
log.dirs=/var/lib/kafka/data

Metadata Design: Log, Snapshots, and Propagation

Metadata updates such as topic creation and ACL changes are appended to the metadata log by the controller leader and replicated by followers. Snapshots are taken at fixed intervals or thresholds, enabling fast sync on restart and for newly joining nodes.

Brokers subscribe to metadata updates from the controllers and update their in-memory cache, providing the equivalent of ZooKeeper watchers entirely inside Kafka.

  • Metadata is order-preserved as an immutable log; applying it yields the final state.
  • Snapshot + incremental replay enables fast bootstrap.
  • Metadata updates become visible only after a majority commit.
ElementRoleOperational point
Metadata RecordA single state-change eventOrdering and idempotent application
Metadata LogOrdered, durable log of recordsFault tolerance and replayability
SnapshotCompacted representation of stateFaster recovery, snapshot generation management

Metadata change flow

Admin API (Topic)Controller Leader(Metadata Log)Append/CommitController FollowersReplicateBrokers (Apply)Push/FetchMetadata change flow

Dedicated metadata directory configuration

# Place the metadata log separately on the controller side
metadata.log.dir=/var/lib/kafka/metadata
# Broker data (topic logs) remains in the usual location
log.dirs=/var/lib/kafka/data

Core Configuration and Key Properties (Co-located 3-Node Example)

The minimum production-ready setup is a 3-node controller quorum. Below is an example with brokers and controllers co-located. Each node uses a unique node.id and address.

It is recommended to isolate the CONTROLLER listener at the network level in production, and apply TLS and authentication.

  • Listing both broker,controller in process.roles co-locates the roles on one node.
  • controller.quorum.voters takes id@host:port entries for every node, comma-separated.
  • Set controller.listener.names to CONTROLLER and define CONTROLLER:// in listeners.
  • inter.broker.listener.name specifies the listener used for inter-broker communication.
PropertyPurposeExample
process.rolesRole assignmentbroker,controller
node.idUnique node ID1
controller.quorum.votersQuorum membership1@n1:9093,2@n2:9093,3@n3:9093
controller.listener.namesListener name used by the controllerCONTROLLER
listenersListener definitionsPLAINTEXT://n1:9092,CONTROLLER://n1:9093
inter.broker.listener.nameUsed for inter-broker trafficPLAINTEXT

Port and listener separation overview

n1: PLAINTEXT 9092  <--> clients/brokers
    CONTROLLER 9093 <--> controller quorum traffic
n2: PLAINTEXT 9092, CONTROLLER 9093
n3: PLAINTEXT 9092, CONTROLLER 9093

server.properties (excerpt for node n1)

process.roles=broker,controller
node.id=1
controller.quorum.voters=1@n1:9093,2@n2:9093,3@n3:9093
controller.listener.names=CONTROLLER
listeners=PLAINTEXT://n1:9092,CONTROLLER://n1:9093
inter.broker.listener.name=PLAINTEXT
advertised.listeners=PLAINTEXT://n1:9092
metadata.log.dir=/var/lib/kafka/metadata
log.dirs=/var/lib/kafka/data

Adoption and Migration: Practical Points

For brand-new clusters, building in KRaft is the cleanest option. The sequence is: config → storage init → startup order (controllers → brokers) → verification.

When migrating from an existing ZooKeeper-based cluster, always check the official procedure and supported versions. Mechanisms aimed at zero- or low-downtime migration have been added incrementally, but version compatibility and constraints can change, so rehearse in a staging environment first.

  • New build: format every node first. Use the same cluster ID on every node.
  • Startup: stabilize the controller quorum first, then start the brokers.
  • Verification: confirm that brokers receive metadata and that topic creation is applied immediately.
  • Migration: strictly follow the supported versions and compatibility matrix in the official docs. Prepare backup and rollback plans.
StepPurposeRepresentative command / focus
Cluster ID generationAssign a unique identifierkafka-storage.sh random-uuid
Storage initializationPrepare the metadata areakafka-storage.sh format -t <UUID> -c server.properties
Startup orderEstablish stable consensuscontrollers → brokers
Health checkConfirm metadata propagationkafka-topics.sh --create / --describe

Greenfield build flow

configformat(storage)start controllersstart brokersverifyGreenfield build flow

Verification commands

# Create a topic
$ KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server n1:9092 --create --topic test --partitions 3 --replication-factor 3
# Verify metadata from the broker's perspective
$ KAFKA_HOME/bin/kafka-topics.sh --bootstrap-server n1:9092 --describe --topic test

Availability, Security, and Monitoring Essentials

Availability hinges on an odd-sized quorum. 3 or 5 is typical, balancing latency and fault tolerance. Using dedicated controller nodes adds the benefits of load isolation and fault-domain separation.

For security, make TLS and authentication mandatory on the CONTROLLER listener. For monitoring, surface controller leader elections, replication lag, snapshot events, and metadata-apply latency.

  • A 3-node quorum tolerates 1 failure; a 5-node quorum tolerates 2.
  • Physically and logically separate the CONTROLLER listener from broker/client listeners.
  • Key metrics: controller replication lag, leader-change count, metadata-apply latency.
Quorum sizeTolerated failuresUse case
10Lab only (not for production)
31Small production
52Mid-sized clusters with high-availability requirements

Majority concept (5-node example)

[1][2][3][4][5]
  ^  ^  ^
Requires agreement from 3 nodes (majority). Tolerates up to 2 failures.

Applying TLS to the CONTROLLER listener (conceptual example)

# Actual keystore / truststore configuration should follow your operational standards
listeners=SSL://n1:9092,CONTROLLER://n1:9093
controller.listener.names=CONTROLLER
listener.security.protocol.map=SSL:SSL,CONTROLLER:SSL
ssl.keystore.location=/path/keystore.jks
ssl.keystore.password=***
ssl.truststore.location=/path/truststore.jks
ssl.truststore.password=***

Check with a Sample Question

CCAAK

問題 1

Which of the following is the most appropriate minimal configuration to correctly form a 3-node co-located (broker+controller) cluster in KRaft mode?

  1. Set process.roles=broker,controller and give each node a unique node.id. Set controller.quorum.voters to 1@n1:9093,2@n2:9093,3@n3:9093, define both PLAINTEXT and CONTROLLER in listeners, and set controller.listener.names=CONTROLLER.
  2. Set zookeeper.connect with 3 nodes and configure broker.id. controller.listener.names can be omitted and node.id is unnecessary.
  3. Set process.roles=controller on only 1 node and process.roles=broker on the other 2. controller.quorum.voters can be left empty because it will be auto-detected.
  4. Set process.roles=broker,controller; then setting controller.listener.names=PLAINTEXT removes the need for a CONTROLLER listener.

正解: A

KRaft does not use ZooKeeper. You list id@host:port entries in controller.quorum.voters, set controller.listener.names to CONTROLLER, and define CONTROLLER:// in listeners. node.id is unique per node. zookeeper.connect and broker.id are not used in KRaft.

Frequently Asked Questions

Can KRaft and ZooKeeper be used together in the same cluster?

No. KRaft mode is designed not to use ZooKeeper at all, and mixing the two is not supported. Build new clusters in KRaft mode, and follow the official migration procedure and supported versions when moving from an existing ZooKeeper-based cluster.

How many nodes should controller.quorum.voters be sized for?

Odd numbers are the rule. Use 3 nodes for small clusters (tolerates 1 failure) or 5 nodes for high availability (tolerates 2 failures). Going larger than needed only increases latency and operational overhead.

What happens if you start a node without initializing storage?

Startup fails with a cluster ID mismatch or uninitialized-storage error. Generate a UUID with kafka-storage.sh and format every node with the same cluster ID before starting.

Check what you learned with practice questions

Practice with certification-focused question sets

無料で問題を解いてみる
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Kafka

Kafka Topics & Partitions: Distribution Fundamentals (2026)

How Kafka topics and partitions enable scale — ordering guar...

Kafka

CCDAK Exam Guide: Confluent Certified Developer (2026)

Complete prep for the CCDAK exam — Producer/Consumer API, St...

Kafka

CCAAK Exam Guide: Confluent Certified Administrator (2026)

Pass the CCAAK exam — cluster management, partitions, securi...

Kafka

Kafka Replicas & ISR: Fault Tolerance Explained (2026)

Replica placement, in-sync replicas (ISR), leader election. ...

Kafka

Kafka Offsets: Commit Modes & Consumer Position (2026)

Offset semantics — auto vs. manual commit, __consumer_offset...

Browse all Kafka articles (101)
© 2026 NicheeLab All rights reserved.