JusDB
Free DB Audit
Database Engineering

Kafka 4.0 Without ZooKeeper: KRaft Migration for CDC Pipelines

Move a ZooKeeper-mode Kafka cluster to Kafka 4.x through the 3.9 bridge: controller quorum design, migration phases, rollback points, and what to check in Debezium and Connect.

JusDB Team
Published November 8, 2026
19 min read

Apache Kafka 4.0 runs only in KRaft mode. If your brokers still have zookeeper.connect in server.properties, there is no 4.x release you can roll onto them. The upgrade notes say it plainly: clusters in ZooKeeper mode "have to be migrated to KRaft mode" before they can upgrade. For teams running change data capture (CDC), that is two projects. First comes a metadata migration on the Kafka 3.9 bridge release. Then comes a 4.x upgrade that needs Java 17 on Kafka Connect workers, drops client protocol versions older than Kafka 2.1, and removes MirrorMaker 1. This guide covers both for a cluster that carries Debezium or other Kafka Connect pipelines. It explains how to size the controller quorum, how the migration phases run, where each rollback point is, and what to check on the Connect side.

TL;DR
  • Kafka 4.0.0 (18 March 2025) removed ZooKeeper mode. As of November 2026, the newest release is 4.4.0 (22 October 2026), and 4.3.2 and 4.2.2 are also listed as supported.
  • ZooKeeper clusters migrate on the 3.9 bridge release. The 3.9 docs require brokers on 3.9.1 or later with inter.broker.protocol.version=3.9; 3.9.2 is the newest 3.x patch.
  • The migration runs in four moves: provision controllers, put brokers in migration mode, restart them as KRaft brokers, then finalize. You can revert to ZooKeeper until you finalize, and not after.
  • Then roll to 4.x on Java 17 and finalize with kafka-features.sh upgrade --release-version. Metadata cannot be downgraded after that step.
  • Connect and Debezium never talk to ZooKeeper, and their offsets live in Kafka topics. The CDC work is Java 17 workers, removed SMT options and APIs, MirrorMaker 1, and dashboards.

Scope and review date: November 2026. This post covers self-managed Apache Kafka clusters in ZooKeeper mode moving to Kafka 4.x through the 3.9 bridge release, with Kafka Connect and Debezium 3.x pipelines attached. Managed services and Kubernetes operators have their own migration procedures, so follow your provider's documentation there. Configs, commands, and version numbers were checked against the Apache Kafka 3.9 and 4.4 documentation and the Debezium release notes on 9 November 2026. They were not run in a lab, so rehearse them on a staging cluster.


What did Kafka 4.0 remove, and which 4.x release is current?

The Kafka upgrade notes open the 4.0 changes with this: "Apache Kafka 4.0 only supports KRaft mode - ZooKeeper mode has been removed." Metadata that lived in ZooKeeper znodes now lives in the __cluster_metadata log, replicated by a Raft quorum of controllers.

The Kafka downloads page lists 4.4.0 (22 October 2026), 4.3.2 (23 October 2026), and 4.2.2 (29 September 2026) as the supported releases. Every 3.x release, including 3.9.2 (21 February 2026), is archived. Treat 3.9 as a place to migrate, not a place to stay.

bash
# On each broker: which mode is this cluster in?
grep -E '^(process\.roles|zookeeper\.connect|broker\.id|node\.id|inter\.broker\.protocol\.version)=' \
  /etc/kafka/server.properties

Interpretation: a zookeeper.connect line with no process.roles means ZooKeeper mode, and you need the full migration below. A process.roles=broker line means the cluster is already KRaft, and you can skip to the 4.x upgrade section. KRaft clusters older than 3.3 should go to 3.9.x first, because 4.x accepts upgrades only from 3.3.x onwards.

ZooKeeper mode and KRaft mode side by side

Switch modes to see what changes in configs, tooling, and operations

ZooKeeper mode

AreaZooKeeper mode
Where metadata livesZooKeeper ensemble (znodes)
ControllerOne broker, elected through the /controller znode
Node identitybroker.id, optionally auto-generated
Version gateinter.broker.protocol.version
How nodes find metadatazookeeper.connect
ACL authorizerkafka.security.authorizer.AclAuthorizer
Change advertised.listenersDynamic config
Supported releases3.9.x and earlier

KRaft mode

AreaKRaft mode
Where metadata lives__cluster_metadata log on the controller quorum
Controller3 or 5 dedicated controllers; the Raft leader is active
Node identitynode.id; broker.id is deprecated in 4.4 and removed in 5.0
Version gatemetadata.version, set with kafka-features.sh
How nodes find metadatacontroller.quorum.bootstrap.servers (dynamic) or controller.quorum.voters (static)
ACL authorizerorg.apache.kafka.metadata.authorizer.StandardAuthorizer
Change advertised.listenersRestart the node
Supported releases3.3.x onwards; the only mode from 4.0

Tooling breaks first: any script, exporter, or admin UI that reads ZooKeeper directly stops working once ZooKeeper is gone. Inventory them before the migration, not after finalize.

What is the upgrade path from a ZooKeeper cluster to Kafka 4.x?

Three stages, each with its own rolling restarts. The 4.4 KRaft operations page says: "The last bridge release is Kafka 3.9." The 3.9 KRaft documentation, updated in October 2026, says brokers "must be upgraded to software version 3.9.1" and run with inter.broker.protocol.version set to 3.9 before the migration starts.

  1. Bridge. Upgrade the ZooKeeper-mode cluster to 3.9.x and raise the inter-broker protocol to 3.9. Use 3.9.2: it adds the KIP-1252 compatibility flag for an AlterConfigPolicy inconsistency between the two modes.
  2. Migrate. Move metadata from ZooKeeper to a KRaft controller quorum, still on 3.9.
  3. Upgrade. Roll the KRaft cluster from 3.9 to 4.x and finalize the new metadata version. The 4.4 notes accept servers "from any version 3.3.x through 4.3.0", so 3.9 can go straight to 4.4.
bash
# Record the cluster ID and the IBP before you start
bin/zookeeper-shell.sh zk-1:2181 get /cluster/id
grep '^inter.broker.protocol.version=' /etc/kafka/server.properties

Interpretation: the cluster ID returned here must be reused when you format the controllers. If the IBP is not 3.9 on every broker, finish that rolling restart first. The 3.9 docs warn that changing the metadata version during the migration "may break the cluster".

Plan Java 17 during the bridge stage

The 3.9 Java page lists Java 8, 11, 17, and 21 as fully supported, while 4.x brokers, Connect, and tools require Java 17. Moving brokers and Connect workers to Java 17 while still on 3.9 keeps the JDK change out of the 4.x upgrade window.

How should you size and place the KRaft controller quorum?

A KRaft controller quorum needs a majority alive to accept metadata changes. The 3.9 and 4.4 docs give the same rules. Run 3 or 5 controllers: 3 tolerate one failure and 5 tolerate two, because tolerating N failures takes 2N + 1 controllers. Combined broker and controller nodes are "not recommended in critical deployment environments". For a typical cluster, the docs estimate that 5 GB of memory and 5 GB of disk for the metadata log directory is sufficient.

Place each controller in a different failure domain, such as one per availability zone, so the quorum survives a zone outage. Controller node.id values share one namespace with broker IDs, so pick a range no broker uses (the docs use 3000 and up).

ini
# controller.properties for a 3-controller static quorum in migration mode (Kafka 3.9)
process.roles=controller
node.id=3000
controller.quorum.voters=3000@ctl-0:9093,3001@ctl-1:9093,3002@ctl-2:9093
controller.listener.names=CONTROLLER
listeners=CONTROLLER://:9093
# Enable the migration
zookeeper.metadata.migration.enable=true
zookeeper.connect=zk-1:2181,zk-2:2181,zk-3:2181
# Lets the KRaft controller send RPCs to ZooKeeper-mode brokers
inter.broker.listener.name=PLAINTEXT

Interpretation: the 3.9 sample uses a single standalone controller with controller.quorum.bootstrap.servers, a dynamic quorum. A static quorum lists every voter in controller.quorum.voters. Static cannot become dynamic on 3.9, but the 4.4 docs show the switch from release version 4.1 by upgrading kraft.version to 1.

Where the controller quorum sits in a CDC pipeline

Debezium and Connect talk only to brokers; ZooKeeper drops out after finalize

KRaft controller quorum, brokers, and a Debezium pipeline Step 1: Debezium tasks in Kafka Connect read the source database's binlog or write-ahead log. Step 2: Connect produces change events, and stores its offsets, configs, and status, as topics on the Kafka brokers. Step 3: the brokers fetch cluster metadata from the KRaft controller quorum of three or five voters. Step 4: during the migration only, the controllers copy metadata from ZooKeeper once and then keep writing to it until the migration is finalized, after which ZooKeeper is retired. Source database binlog or WAL Kafka Connect Debezium tasks Kafka brokers process.roles=broker ZooKeeper retired after finalize KRaft controllers 3 or 5 voters 1 2 3 metadata log 4 offsets, configs, status are ordinary topics migration only: copy once, then dual-write until finalize

Metadata, not data: the migration moves cluster metadata into the quorum. Topic data, including Connect's internal topics and Debezium's schema history topic, stays on the brokers.

How do you run the ZooKeeper to KRaft migration on Kafka 3.9?

The 3.9 docs name the phases: initial (all brokers in ZooKeeper mode), initial metadata load, hybrid (some brokers still in ZooKeeper mode under a KRaft controller), dual-write (all brokers in KRaft mode, the controller still writes to ZooKeeper), and finalized. Each move below is a rolling restart, so partitions stay available as long as replication factors and min.insync.replicas allow one broker down.

ZooKeeper to KRaft migration, then Kafka 4.x

Nine steps; steps 7 and 9 are one-way, and step 7 is the last ZooKeeper rollback point

  1. Bridge to 3.9.x with IBP 3.9

    Rolling upgrade of the ZooKeeper-mode brokers to 3.9.1 or later, then set the inter-broker protocol to 3.9.

    inter.broker.protocol.version=3.9
  2. Format and start the controllers

    Reuse the existing cluster ID. Controllers start with migration enabled and a ZooKeeper connection.

    bin/kafka-storage.sh format \ --cluster-id <zk-cluster-id> \ -c config/kraft/controller.properties
  3. Put brokers in migration mode

    Add the migration flag and controller settings, then roll the brokers. The metadata copy starts after the last broker restarts.

    zookeeper.metadata.migration.enable=true
  4. Confirm the metadata load

    The active controller logs one INFO line when the copy finishes. The cluster is now in the hybrid phase.

    grep 'Completed migration of metadata' \ /var/log/kafka/server.log
  5. Restart brokers as KRaft brokers

    Replace broker.id with node.id (same value), add process.roles=broker, remove ZooKeeper settings. Dual-write starts when the last one is done.

    process.roles=broker node.id=0
  6. Burn in under dual-write

    The docs note that some users wait a week or two here. Run month-end CDC loads and connector restarts in this window.

  7. Finalize the migration

    No return to ZooKeeper

    Remove the migration flag and ZooKeeper settings from each controller and restart them one at a time.

    # zookeeper.metadata.migration.enable=true # zookeeper.connect=zk-1:2181
  8. Roll every node to Kafka 4.x

    Java 17 or later on every node. One node at a time: stop, swap the binaries, start, check under-replicated partitions.

  9. Finalize the 4.x metadata version

    No metadata downgrade

    Only after the 4.x cluster has run cleanly. The 4.0 notes say metadata downgrade is not supported after this.

    bin/kafka-features.sh \ --bootstrap-server broker-0:9092 \ upgrade --release-version 4.4

Rollback point: until step 7, the 3.9 docs give a revert path to ZooKeeper for every completed step. After step 7, the docs offer no way back to ZooKeeper, and after step 9 a 4.x cluster cannot lower its metadata version.

Broker settings for migration mode

Adapted from the 3.9 sample. broker.id must be an explicit non-negative integer even when broker.id.generation.enable is on.

ini
# server.properties, ZooKeeper-mode broker entering migration mode (Kafka 3.9)
broker.id=0
listeners=PLAINTEXT://:9092
advertised.listeners=PLAINTEXT://broker-0:9092
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
inter.broker.protocol.version=3.9
zookeeper.metadata.migration.enable=true
zookeeper.connect=zk-1:2181,zk-2:2181,zk-3:2181
controller.quorum.voters=3000@ctl-0:9093,3001@ctl-1:9093,3002@ctl-2:9093
controller.listener.names=CONTROLLER

Interpretation: use the same quorum setting as the controllers, and add the controller listener to listener.security.protocol.map. With TLS or SASL, that entry must match the controllers' listener.

Broker settings for step 5

ini
# server.properties, same broker restarted in KRaft mode
process.roles=broker
node.id=0
listeners=PLAINTEXT://:9092
advertised.listeners=PLAINTEXT://broker-0:9092
listener.security.protocol.map=PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT
controller.quorum.voters=3000@ctl-0:9093,3001@ctl-1:9093,3002@ctl-2:9093
controller.listener.names=CONTROLLER
# Removed: inter.broker.protocol.version, zookeeper.metadata.migration.enable,
# zookeeper.connect, control.plane.listener.name
# If you used AclAuthorizer:
authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer

Interpretation: node.id must equal the old broker.id; the docs say the broker must keep the same ID. KRaft uses the metadata.version feature flag instead of an IBP.

Known migration traps in the 3.9 docs

The migration can stall if the ZooKeeper Security Migration Tool was ever run (KAFKA-19480); the workaround is to delete the malformed /migration znode. A broker with multiple log directories shuts down on any directory failure during the migration, and can migrate only after the directories are repaired (KAFKA-16431).

Where are the rollback points, and how do you revert?

The 3.9 docs include a revert table keyed on the last step you fully completed. Before brokers enter migration mode, you only deprovision the controllers. After that, reverting needs ZooKeeper edits and one or two broker rolls. Once the migration is finalized, the docs say "you cannot revert".

Last completed stepHow to revert
Bridge to 3.9 (step 1)Nothing to undo for the migration; the cluster is still in ZooKeeper mode
Controllers provisioned (step 2)Deprovision the KRaft controller quorum
Brokers in migration mode (steps 3 and 4)Deprovision the quorum, fix the controller epoch, delete /controller and /migration, remove the migration configs, roll brokers once
Brokers in KRaft mode (step 5)Restore ZooKeeper configs and roll brokers, deprovision the quorum, run the same ZooKeeper steps, then roll brokers a second time
Finalized (step 7)Not possible
bash
# Revert from step 3 or later, right after deprovisioning the controllers
bin/zookeeper-shell.sh zk-1:2181 get /controller_epoch
bin/zookeeper-shell.sh zk-1:2181 get /migration   # note kraft_controller_epoch
# Only if the KRaft epoch is higher: pick a value above it
bin/zookeeper-shell.sh zk-1:2181 set /controller_epoch 42
bin/zookeeper-shell.sh zk-1:2181 delete /controller
bin/zookeeper-shell.sh zk-1:2181 delete /migration

Interpretation: the epoch check is new in the October 2026 revision of the docs (KAFKA-20488). Partition state znodes carry the KRaft controller epoch during the migration. A ZooKeeper controller elected with a lower epoch fails partition state changes, because it assumes another controller with a higher epoch exists. The value 42 is a placeholder. Run these commands quickly: the cluster has no controller until /controller is deleted.

Keep the migration flag on the first revert roll

When reverting from step 5, the docs say zookeeper.metadata.migration.enable must stay true during the first broker roll and come out only on the second. Write both rolls into the runbook before you start.

What breaks when you move from KRaft 3.9 to Kafka 4.x?

On a finalized KRaft 3.9 cluster, the 4.x upgrade is a rolling binary swap plus kafka-features.sh. What breaks is mostly what connects to the cluster:

  • Java. Clients and Kafka Streams need Java 11 or later. Brokers, Connect, and tools need Java 17. The 4.4 Java page lists Java 17, 21, and 25 as fully supported and recommends the newest LTS.
  • Old protocol versions. Brokers must be 2.1 or later before Java clients go to 4.0, and Java clients (including Connect and Streams) must be 2.1 or later before brokers go to 4.0. Non-Java clients need their own check against KIP-896.
  • Removed configs and tools. log.message.format.version, message.format.version, offsets.commit.required.acks, and delegation.token.master.key are gone, and so is MirrorMaker 1.
  • Logging. Log4j moved to Log4j2. The notes point to log4j-transform-cli to convert existing Log4j files.
  • Group and transaction protocols. The new consumer rebalance protocol (KIP-848) and the transactions server-side defense (KIP-890) turn on when the 4.0 upgrade is finalized. Once consumer groups use the new protocol, the cluster can only be downgraded to 3.4.1 or newer.
bash
# On 3.9, before the 4.x roll: are old protocol versions still in use?
bin/kafka-run-class.sh org.apache.kafka.tools.JmxTool \
  --jmx-url service:jmx:rmi:///jndi/rmi://broker-0:9999/jmxrmi \
  --object-name 'kafka.network:type=RequestMetrics,name=DeprecatedRequestsPerSec,*' \
  --one-time true

Interpretation: KIP-896 added DeprecatedRequestsPerSec in 3.7, tagged by request, API version, and client software name and version. KIP-896 says that if it is 0, "the upgrade is safe." A non-zero value names the client library to upgrade. Check every broker over a full business cycle, because batch jobs and old CDC consumers may connect only once a day.

bash
# After every node runs 4.x and the cluster looks healthy
bin/kafka-features.sh --bootstrap-server broker-0:9092 describe
bin/kafka-features.sh --bootstrap-server broker-0:9092 upgrade --release-version 4.4

Interpretation: describe shows the finalized metadata.version and kraft.version. The upgrade raises them. The 4.0 notes say metadata downgrade is not supported because 4.0 carries metadata changes, so this is the second one-way step in the project.

What changes for Debezium and Kafka Connect pipelines?

Kafka Connect workers are Kafka clients. They reach the cluster through bootstrap.servers and never through ZooKeeper, so the migration itself needs no Connect config change. Connect keeps connector offsets, configs, and status in its three internal topics. Debezium keeps its database schema history in a topic as well. The migration moves cluster metadata only, so those topics and their data stay where they are.

What the pipeline does feel is the broker rolls. Each restart moves partition leaders, and Connect's clients retry. Source connectors deliver at least once unless exactly.once.source.support is enabled, so a task restart between offset flushes can repeat a few change events. Keep sinks idempotent, for example with upserts keyed on the primary key.

bash
# Before each roll: record Debezium's committed source offsets (Connect 3.5+)
curl -s http://connect-0:8083/connectors/inventory-cdc/offsets | jq .
curl -s http://connect-0:8083/connectors/inventory-cdc/status | jq '.tasks[].state'

Interpretation: the offsets response holds the binlog position, LSN, or resume token per source partition. If a connector comes back after a roll at an older position than the one recorded, you will see re-delivery. The Connect user guide documents PATCH and DELETE on the same endpoint, which only work while the connector is stopped with PUT /connectors/{name}/stop.

Moving the Connect workers to 4.x

You can upgrade brokers first and keep Connect workers on a 3.x release, since 3.x clients are newer than 2.1. When the workers move to 4.x, check these:

  • Debezium version. The Debezium 3.7 release notes say 3.7.1.Final (6 November 2026) "has been built against Kafka Connect 4.3.1 and has been tested with version 4.3.1 of the Kafka brokers". The Debezium releases page lists Java 17+ for connectors and Kafka Connect 3.1 and later. Debezium 2.7 lists Java 11 and Connect 2.x and 3.x, so 4.x workers are outside its tested range.
  • Removed SMT options. whitelist and blacklist were removed from ReplaceField; use include and exclude.
  • Removed task APIs. SinkTask.onPartitionsRevoked(Collection), onPartitionsAssigned(Collection), and SourceTask.commitRecord(SourceRecord) are gone. Custom or older third-party connectors that override them need a newer build.
  • Producer defaults. The default linger.ms changed from 0 to 5 in 4.0. If end-to-end latency matters, set producer.linger.ms in the worker config and measure.
  • MirrorMaker. MirrorMaker 1 is removed, so CDC topics copied to a second cluster need MirrorMaker 2. Its topics.blacklist, groups.blacklist, and config.properties.blacklist options become *.exclude.
bash
# Find connector configs that use removed ReplaceField or MirrorMaker 2 options
for c in $(curl -s http://connect-0:8083/connectors | jq -r '.[]'); do
  curl -s "http://connect-0:8083/connectors/$c/config" \
    | jq -r --arg c "$c" 'to_entries[]
        | select(.key | test("whitelist|blacklist"))
        | "\($c): \(.key)"'
done

Interpretation: every hit is a config to edit before it runs on a 4.x worker, because the removed key no longer applies its filter. MirrorMaker 2 running in dedicated mode reads mm2.properties instead, so grep that file too.

What should you monitor during and after the migration?

ZooKeeper-mode dashboards go quiet after the migration. The KRaft vs ZooKeeper page lists the controller metrics that exist only in ZooKeeper mode, such as ControllerChannelManager queue metrics and ControllerStats rate metrics. During the migration, the 3.9 monitoring docs give you a state gauge to track.

MetricWhat to watch
kafka.controller:type=KafkaController,name=ZkMigrationState2 = PRE_MIGRATION, 1 = MIGRATION, 3 = POST_MIGRATION (0 = created in KRaft, 4 = ZooKeeper controller)
kafka.controller:type=KafkaController,name=MigratingZkBrokerCountBrokers still in ZooKeeper mode; 0 before you finalize
kafka.controller:type=KafkaController,name=ActiveControllerCountExactly one node across the cluster reports 1
kafka.server:type=raft-metrics,name=current-leaderThe quorum leader ID; -1 means unknown
kafka.server:type=raft-metrics,name=commit-latency-maxTime to commit a metadata record; alert on sustained growth
kafka.server:type=broker-metadata-metrics,name=last-applied-record-lag-msHow far each broker's metadata view trails the log
bash
# Quorum health from the command line (3.9 and 4.x)
bin/kafka-metadata-quorum.sh --bootstrap-server broker-0:9092 describe --status
bin/kafka-metadata-quorum.sh --bootstrap-server broker-0:9092 describe --replication

Interpretation: --status summarizes the quorum and --replication lists each voter's and observer's log end offset and lag. Run both before and after every controller restart.

Checking the KRaft quorum after the migration

Illustrative output, abbreviated, shaped like the sample in the Kafka KRaft docs

kafka@broker-0:~
$ bin/kafka-metadata-quorum.sh --bootstrap-server broker-0:9092 \ describe --statusClusterId: fMCL8kv1SWm87L_Md-I2hgLeaderId: 3001LeaderEpoch: 7HighWatermark: 184213MaxFollowerLag: 0 okMaxFollowerLagTimeMs: 0CurrentVoters: [{"id": 3000, ...}, {"id": 3001, ...}, {"id": 3002, ...}]CurrentObservers: [{"id": 0, ...}, {"id": 1, ...}, {"id": 2, ...}]

What healthy looks like: one leader, every controller listed as a voter, every broker listed as an observer, and follower lag near 0. The real output also prints each node's directoryId and endpoints; the field list matches the KRaft operations docs.

On the Connect side, watch task state and Debezium's MilliSecondsBehindSource metric. A lag spike during a broker roll is expected; a task stuck in FAILED afterwards is not. For Connect metrics, see Kafka Connect: JDBC source, SMTs, dead letter queues, and connector monitoring.

Frequently asked questions

Can I upgrade a ZooKeeper-based Kafka cluster directly to Kafka 4.0?

No. Kafka 4.0 and later run only in KRaft mode. A ZooKeeper-mode cluster must first upgrade to the 3.9 bridge release (3.9.1 or later, with inter.broker.protocol.version set to 3.9), migrate its metadata to a KRaft controller quorum, and finalize that migration. Only then can it roll to a 4.x release such as 4.4.0.

Can I roll back a ZooKeeper to KRaft migration?

Yes, until you finalize it. The Kafka 3.9 documentation gives a revert procedure for each completed step: deprovision the controllers, fix the ZooKeeper controller epoch, delete the /controller and /migration znodes, and roll the brokers back to ZooKeeper settings. After the controllers leave migration mode, reverting is not possible, so burn in under dual-write first.

How many KRaft controllers does a production Kafka cluster need?

Three or five dedicated controllers. A majority must be alive, so three controllers tolerate one failure and five tolerate two. Kafka's documentation advises against combined broker and controller nodes in critical environments, and estimates that 5 GB of memory and 5 GB of metadata log disk is sufficient for a typical cluster.

Does the KRaft migration affect Debezium connector offsets?

Not directly. Kafka Connect stores Debezium's source offsets, configs, and status in Kafka topics, and the migration moves only cluster metadata. Broker restarts during the migration can cause connector task retries and, with at-least-once delivery, some duplicate change events. Record offsets before each roll and keep sinks idempotent.

Key takeaways

  • ZooKeeper clusters go 3.9.x (3.9.1 or later, IBP 3.9), then the migration, then 4.x. Kafka 4.4.0 is the newest release as of November 2026.
  • Run 3 or 5 dedicated controllers with node IDs no broker uses, one per failure domain.
  • Two steps are one-way: finalizing the migration and finalizing the 4.x metadata version. Burn in before each.
  • Before the 4.x roll, check DeprecatedRequestsPerSec, move brokers and Connect workers to Java 17, and replace MirrorMaker 1.
  • Connect and Debezium offsets survive the migration as ordinary topics. Plan for retries and duplicates during rolls, and confirm Debezium supports your Connect worker version.

Plan your Kafka KRaft migration with JusDB

JusDB's Debezium CDC team can review your ZooKeeper-mode cluster and the Connect pipelines on it. We can plan the controller quorum and write a migration runbook with the revert steps for each phase. We can also check your connectors, SMTs, and clients against the 4.x changes before the upgrade.

For the wider cutover, our database migration service can help plan and run the change windows alongside your team. Bring your broker and Connect versions, the connector inventory, and your maintenance windows. Your team approves every production change.

Share this article

Database engineering notes

Articles like this one, in your inbox. No spam, unsubscribe anytime.

JusDB Team

Official JusDB content team

Keep reading

ProxySQL 3.0: PostgreSQL Support and What Changed for MySQL Users

Find out which ProxySQL 3.0 release to run, what its PostgreSQL support covers, what changed for MySQL users since 2.7, and how to upgrade a node or a cluster with a rollback point.

PostgreSQL20 minNov 7, 2026
Read

Percona Operators for MySQL, PostgreSQL, and MongoDB Compared

Compare the four Percona Kubernetes operators as of November 2026: current versions, GA and tech preview features, failover, proxies, backups and PITR, upgrades, TLS, and PMM.

PostgreSQL20 minNov 4, 2026
Read

SQL Server 2016 Is Out of Support: Upgrade Path to 2022 or 2025

SQL Server 2016 lost support on 14 July 2026. Weigh ESUs, pick SQL Server 2022 or 2025, check the upgrade path, and run a side-by-side cutover with a safe compatibility level plan.

SQL Server19 minNov 2, 2026
Read