Why now
Kafka 4.0 removed ZooKeeper mode. You must migrate on a 3.x release that supports migration (3.9 is the bridge release) before upgrading to 4.x. Check the exact supported path for your version in the Kafka upgrade notes.
1. Pre-checks
- Brokers on a migration-capable version (upgrade first if not), with
inter.broker.protocol.versionat that version. - Note the cluster ID:
zookeeper-shell.sh zk1:2181 get /cluster/id
- Check unsupported features for your version (some older features, like certain SCRAM or JBOD scenarios, had restrictions in early migration releases).
- Back up ZooKeeper data and take a configuration snapshot of brokers.
2. Deploy KRaft controllers in migration mode
Provision 3 (or 5) controller nodes. Format their storage with the existing cluster ID:
kafka-storage.sh format -t <cluster-id> -c /etc/kafka/controller.properties
Controller config (key lines):
process.roles=controller
node.id=3000
controller.quorum.voters=3000@ctrl1:9093,3001@ctrl2:9093,3002@ctrl3:9093
controller.listener.names=CONTROLLER
listeners=CONTROLLER://:9093
zookeeper.metadata.migration.enable=true
zookeeper.connect=zk1:2181,zk2:2181,zk3:2181
Newer releases use controller.quorum.bootstrap.servers and dynamic quorums; follow the documentation for your version.
Start controllers. The active controller waits for brokers to register before copying metadata from ZooKeeper.
3. Brokers: enable migration (rolling)
On each broker add:
zookeeper.metadata.migration.enable=true
controller.quorum.voters=3000@ctrl1:9093,3001@ctrl2:9093,3002@ctrl3:9093
controller.listener.names=CONTROLLER
listener.security.protocol.map=...,CONTROLLER:PLAINTEXT
Roll brokers one at a time, waiting for under-replicated partitions to return to zero between restarts. When all are restarted, the controller migrates metadata to KRaft (log: "Completed migration of metadata from ZooKeeper to KRaft"). The cluster is now in dual-write mode.
4. Brokers: move to KRaft mode (rolling)
Replace broker.id with node.id (same value), add process.roles=broker, and remove zookeeper.connect and the migration flag. Roll brokers again.
5. Finalise
Remove the migration settings from the controllers and restart them. ZooKeeper is no longer used.
Caution
Rollback to ZooKeeper is possible only before finalising (step 5), following the documented reverse steps. After finalisation, there's no way back — keep the cluster in the pre-finalised state for a soak period.
6. Verify
kafka-metadata-quorum.sh --bootstrap-server kafka1:9092 describe --status
kafka-topics.sh --bootstrap-server kafka1:9092 --describe --under-replicated-partitions
Then update monitoring (controller metrics move to the controller nodes), runbooks and tooling that called ZooKeeper directly (--zookeeper flags were removed long ago — scripts using them must use --bootstrap-server).
Checklist
- On a migration-capable 3.x version; cluster ID recorded
- Controllers formatted with the existing cluster ID
- Brokers rolled with migration enabled; metadata migrated
- Brokers rolled into KRaft mode; soak period completed
- Finalised; ZooKeeper decommissioned; tooling updated