Skip to content
Kafka Is Fun

Lesson 2 of 6

Migrating from ZooKeeper to KRaft

2 min read

  • ZooKeeper mode: removed in 4.0

Overview

You migrate on 3.9, not on 4.0, because the migration needs a release that still speaks ZooKeeper. The cluster moves through named phases. It starts in the initial phase, with every broker in ZooKeeper mode. A new KRaft controller quorum then loads the metadata from ZooKeeper. While some brokers are still in ZooKeeper mode and a KRaft controller exists, you are in the hybrid phase. When every broker runs in KRaft mode but the controller still writes metadata to ZooKeeper as well, you are in the dual-write phase. Finalizing stops those writes. Until you finalize you can roll back; after, you cannot, which is why the documentation notes that some people wait a week or two first. The 3.9 documentation also lists traps: do not change the metadata version during the migration; KRaft node ids share a namespace with broker ids, so they must differ; the migration can stall if the ZooKeeper Security Migration Tool was run before (delete the /migration node); a static controller quorum cannot be converted to a dynamic one; and JBOD with several log directories is listed as a feature KRaft does not fully implement, so a cluster that depends on it cannot migrate yet.

Analogy

Key configuration

Migration steps (from the 3.9 documentation)properties
# 1. Prepare: brokers on 3.9.x with inter.broker.protocol.version=3.9

# 2. Provision KRaft controllers, using the ZooKeeper cluster's id
process.roles=controller
node.id=3000                 # must differ from every broker.id
controller.quorum.bootstrap.servers=localhost:9093
controller.listener.names=CONTROLLER
listeners=CONTROLLER://:9093
zookeeper.metadata.migration.enable=true
zookeeper.connect=localhost:2181
inter.broker.listener.name=PLAINTEXT

bin/kafka-storage.sh format --standalone --cluster-id=<zk-cluster-id> -c config/kraft/controller.properties

# 3. Rolling restart of every broker in migration mode
zookeeper.metadata.migration.enable=true
controller.quorum.bootstrap.servers=localhost:9093
controller.listener.names=CONTROLLER     # also add it to listener.security.protocol.map
# Migration starts when the last ZooKeeper broker restarts. The active controller logs:
#   Completed migration of metadata from Zookeeper to KRaft

# 4. Roll each broker into KRaft, one at a time
process.roles=broker
node.id=<its old broker.id>
# remove zookeeper.connect and zookeeper.metadata.migration.enable; do not set the IBP
# authorizer.class.name: AclAuthorizer -> org.apache.kafka.metadata.authorizer.StandardAuthorizer
# All brokers in KRaft mode: dual-write phase. You can still roll back.

# 5. Finalize: remove zookeeper.metadata.migration.enable from each controller,
#    restarting them one at a time. ZooKeeper can then be switched off.

Production tip

Check yourself

A migrating cluster has every broker in KRaft mode, but the controller still writes metadata to ZooKeeper. What phase is this, and can you still roll back?

Sources

Checked against Kafka 4.3 documentation on Oct 2026. Draft: not yet reviewed by a Kafka specialist. Releases move on, so confirm against the current documentation before you act on a number.

Try it in the simulator

Guided scenarios for this module — the simulator opens with the exercise already running.

Prefer a picture? Open the live cluster in 3D and watch records move between producer, brokers and consumers.