Moving a KRaft cluster from a static to a dynamic controller quorum

Teams running KRaft with a fixed controller.quorum.voters list need a planned path to a dynamic quorum. This page covers the checks, the upgrade order and how controllers are added and removed safely.

On this page

Why a KRaft cluster on a static quorum needs a planned move to dynamic

If your KRaft cluster fixes its controller set in controller.quorum.voters, you can only change that set by restarting the cluster with new configuration, and that is the situation this page addresses. Moving to a dynamic controller quorum lets you add and remove controllers while the cluster keeps running, but the switch itself is a deliberate upgrade of the cluster's metadata layer and has to be planned as one. Before you change anything, confirm the current state: a cluster is on a static quorum when the kraft.version feature level is 0 or absent, and on a dynamic quorum when it is 1 or above. You can read that level with kafka-features.sh describe on a running cluster.

The timing depends on the release you are running. The Kafka downloads page lists 4.3.1, 4.2.2 and 4.1.2 as supported releases, and the time-based release plan supports rolling upgrades from each of the last three releases to the latest version. If your cluster is on an older 3.x line, the upgrade notes recommend first moving KRaft clusters older than 3.3.x to 3.9.x before going to 4.x, and ZooKeeper-based clusters must migrate to KRaft before any 4.x upgrade. Confirm your current version and its supported upgrade path with the vendor documentation before you schedule the work, because the order of the controller change relative to the broker upgrade matters.

Treat the quorum change as a separate maintenance item with its own rollback thinking. Once the feature level is raised, the cluster no longer runs the static membership model you started with, and some steps cannot be undone by simply restarting old binaries. Decide in advance which controllers will be added or retired, confirm that each new controller can catch up before it is counted, and agree who signs off on the metadata version change. The Apache Kafka 4.3 KRaft documentation describes the add and remove controller procedure that the rest of this page follows.

What changes when the quorum becomes dynamic

This table compares a KRaft cluster on a static quorum with one on a dynamic quorum, row by row, from the kraft.version feature level through to the controller commands and the operational steps they change. Read each row as a question to answer about your cluster before you start, and note that the main takeaway is that controller membership stops being a configuration file edit and becomes a metadata operation.

AreaStatic quorumDynamic quorum
kraft.version feature level0 or absent1 or above
How the controller set is definedSet by controller.quorum.voters in each node's configurationTracked in the cluster metadata, with controller.quorum.voters no longer the source of truth
Adding a controllerEdit controller.quorum.voters on every node and restart them in a planned orderStart the new controller, let it catch up, then run kafka-metadata-quorum.sh add-controller
Removing a controllerRemove it from controller.quorum.voters everywhere and restart the remaining nodesRun kafka-metadata-quorum.sh remove-controller before shutting the controller down
Change risk during membership editsConfiguration drift between nodes can leave the quorum inconsistent until every node agreesThe change is recorded in metadata, so the controller set is managed through one command path
How you verify the current stateCompare configuration files across controller nodesCheck kraft.version with kafka-features.sh describe, then review quorum state with kafka-metadata-quorum.sh
Action required before the switchConfirm all controllers share identical controller.quorum.voters valuesConfirm every controller and broker runs a release that supports the target feature level (confirm the exact requirement in the upgrade notes)
Action required after the switchKeep configuration files in sync with any future membership changeUpdate runbooks and automation so membership changes go through the metadata commands, not file edits

How to confirm whether your cluster still uses a static quorum

The first step is to establish which quorum model your cluster runs today, because every later step depends on it. A KRaft cluster uses a static quorum when the kraft.version feature level is 0 or absent, and a dynamic quorum when the level is 1 or above. Run kafka-features.sh with the describe subcommand against any broker or controller endpoint, and look for the kraft.version line in the output. If the line reports level 0, or the feature does not appear at all, the controller set is still defined by controller.quorum.voters in each node's configuration file and cannot be changed with kafka-metadata-quorum.sh.

Before you change anything, record what the cluster looks like so you can compare it after the upgrade. Capture the output of the describe command, the kraft.version and metadata.version levels, the Kafka release every node runs, and the value of controller.quorum.voters on each controller. Also note the node IDs, the listener addresses and the directory log paths for each controller, since a dynamic quorum identifies controllers by ID and directory ID. Store this record with your change ticket. A written baseline makes it possible to prove, later, which controllers were voters before the change and which were added after it.

The exact output layout of kafka-features.sh describe can differ between Kafka releases, so confirm the format against the release your cluster runs before you build any parsing or runbook step around it. Check the KRaft documentation for Kafka 4.3 for the version your nodes match, and run the command on a non-production copy if one exists. If the output does not show a kraft.version line for a cluster you expected to be current, treat that as a finding to resolve with the vendor or your own platform team, not as proof that the cluster is static.

What to prepare before touching controllers

Changing the controller quorum touches the metadata that every broker depends on, so the preparation is as much about records as about commands. The six items below each have an owner and an artifact you should be able to show before the first change.

Verified metadata and broker backup

Take a backup of the metadata log and snapshots from the controllers, and keep broker data backups for the topics you cannot rebuild. The platform or database owner runs the backup, but the evidence that matters is a restore test into an isolated environment, with the restored cluster reporting the same metadata state. Record the backup time, the source controller, and the checksums. Without a tested restore, a failed quorum change has no clean way back.

Controller host and listener inventory

List every controller and combined broker-controller host with its node ID, controller listener address, port, and certificate identity. Compare that list with controller.quorum.voters on each node so no node disagrees about the current membership. The infrastructure team owns this inventory, and the evidence is a dated export from each node's configuration, stored with the change ticket.

Staging cluster that mirrors production

Build a staging cluster with the same controller count, listener layout, TLS and SASL settings, and Kafka version as production, then run the full sequence there first. Staging with three nodes on one host does not reproduce network or disk behavior, so the team should confirm which differences remain. The platform team owns the environment, and the evidence is a written record of each step run and the result.

Client and tooling compatibility checks

Inventory the scripts, automation, monitoring checks and admin clients that call kafka-metadata-quorum.sh or kafka-features.sh, and confirm they run against the target version. Also confirm that any tool reading controller endpoints from static configuration will be updated when membership changes. The owners are the teams that maintain those tools, and the evidence is a list of tested commands with their outputs.

Change window and rollback decision

Agree a change window with the service owners, the on-call lead and the people who can approve a stop. Write down who decides to halt, what condition triggers a halt, and who executes the rollback steps from the backup. Keep the approval, the named contacts and the stop criteria in the change record. Confirm the window length with your own change board, since no timing is set by the project.

Operator access and separation of duties

Confirm that the operators performing the change have shell access to every controller, the credentials needed for TLS and SASL, and read access to monitoring during the window. Where the change requires a second person to review commands, assign that role before the window opens. Security owns the access grants, and the evidence is the access list with the date each grant was verified.

The upgrade sequence from static to dynamic quorum

This sequence takes a KRaft cluster from a static controller quorum defined by controller.quorum.voters to a dynamic quorum where controllers can be added and removed while the cluster stays online. Each step ends with a check, so you know whether to continue, pause, or roll back before the next change.

  1. Confirm the running release and kraft.version

    Record the Kafka version on every broker and controller, then run kafka-features.sh describe against the cluster to read the current kraft.version feature level. A level of 0, or no entry, means the cluster uses a static quorum. Save this output with your change record. If any node runs a release older than 3.3.x, plan a move to 3.9.x first, as the upgrade notes recommend. If the output does not match your inventory, stop and resolve the difference before changing anything.

  2. Upgrade the cluster to a supported 4.x release

    If the cluster is not already on a supported 4.x release, perform a rolling upgrade to the latest supported version. The time-based release plan supports rolling upgrades from each of the last three releases to the latest version. Upgrade controllers and brokers one node at a time, waiting for each node to rejoin and for under-replicated partitions to clear before moving on. Confirm client compatibility beforehand. Until you finalize in the next step, the release can still be rolled back to the previous binaries, so confirm the rollback limits in the upgrade notes for your starting version.

  3. Finalize the kraft.version feature level

    Raise kraft.version to 1 with the feature upgrade command in kafka-features.sh, which turns on dynamic quorum support in the metadata log. Finalizing is the point where downgrade becomes limited, so treat it as a planned change with a named owner and a backout plan. After the command completes, run kafka-features.sh describe again and confirm that kraft.version reports level 1 on the cluster. Do not add or remove controllers until this check passes.

  4. Add new controllers after they catch up

    Start each new controller with its node ID, storage directory and controller listener configured for the cluster, but do not yet give it a voting role in your plan. Run kafka-metadata-quorum.sh add-controller for the new node, and use describe with the replication option to confirm it has caught up to the leader's metadata log. Add controllers one at a time. If a new node does not catch up, leave it out of the quorum, fix the cause, and check again. The result is a larger voter set with every new member replicated.

  5. Verify the quorum before removing controllers

    Check the quorum state with kafka-metadata-quorum.sh describe --status and confirm that a leader is elected and that the expected voters appear in the voter set. Run a test that writes and reads a topic to confirm metadata operations succeed. This step is your last clean rollback point for the membership change. Keep the retired controllers running and healthy until the verification passes, because removing a voter before confirming the new set is the most likely way to lose quorum during the change.

  6. Remove retired controllers before shutdown

    For each retired controller, run kafka-metadata-quorum.sh remove-controller before stopping the process. Remove one controller at a time and repeat the status check after each removal to confirm a leader and the expected voter count. Only shut down a controller after its removal is confirmed. If you need to stop a node earlier, the dynamic quorum procedure is the supported route, so do not stop a voter first and clean up later. Record the final voter set in your change record.

How to add and remove controllers without downtime

Add controllers one at a time, and only after each new controller has caught up with the metadata log. Once kraft.version shows 1 or higher, the quorum is dynamic and membership changes are made with kafka-metadata-quorum.sh. Start the new controller with its own node ID, directory and listener, and let it replicate. Then run add-controller against the active quorum, using the bootstrap server and the command configuration file for your security settings. Do not run add-controller before the new node is ready, because the controller will be counted toward the quorum while it still lacks data.

Remove a controller with `remove-controller` before you stop its process. The sequence is the reverse of adding: confirm the controller you are retiring is not the only path to the leader, run remove-controller with its node ID and directory ID, and only then shut it down. Stopping a voter first leaves the quorum counting a member that no longer answers, which is the situation that can cost you majority. Keep the controller's directory ID from the describe output, since removal depends on it. Plan the number of controllers so that each step still leaves a majority of voters online, and confirm that number with your own sizing before you begin.

Verify every step with describe output, not with the command's exit status alone. Run kafka-metadata-quorum.sh describe --status to see the leader, the current voters and the highest log offset. Run describe --replication to confirm each voter's log end offset is close to the leader's before you proceed. After each change, repeat both commands and confirm the voter list matches your plan. Record the output at each step so that a failed change can be rolled back to a known membership. Confirm the exact flags and output fields against the KRaft documentation for the version you run, since the output format can change between releases.

Testing and rollback by environment

Staging is where the team proves the sequence works on a copy of production that matches its controller count, listener configuration and authorization setup. Record the current state with kafka-features and kafka-metadata-quorum first, then run the full sequence end to end, not only the individual commands. The KRaft documentation is the reference for each command's expected output.

  • Starting state. Confirm kraft.version reads 0 or is absent on staging, and record the voter list from controller.quorum.voters so you can compare results after each step.
  • Controller add and remove. Add a new controller only after it has caught up, then remove a retired controller before it shuts down. Time each step against the runbook.
  • Client behavior. Run producers, consumers and admin tooling through every stage and watch for metadata errors during leader changes.
  • Failure injection. Stop one controller during the membership change and confirm the cluster elects a leader and keeps serving traffic.
  • Rollback drill. Practice the rollback steps you intend to use in production, and record any step you could not complete.

Record timings, errors and deviations from the runbook before production approval is requested.

What makes a controller quorum change take longer

This table lists the factors that change how much work a move from a static to a dynamic controller quorum involves. Read each row as a direction: the factor adds effort or time as it grows, and the right column names what to check before you schedule the change. The main takeaway is that the controller count matters less than the tooling and configuration that still points at controller.quorum.voters.

DriverWhy it changes effortHow to keep it manageable
Number of controllers and brokersMore controllers mean more add-controller and catch-up checks, and more nodes to restart with updated configuration. Broker count adds rollout time because each broker's configuration must be verified.Plan the controller sequence in a written runbook and confirm each controller is caught up before moving to the next.
Prior versionClusters on older KRaft releases may need an intermediate upgrade before the feature level can be raised. Confirm the release path in the Kafka upgrade notes before planning the quorum change.Confirm your current release and the supported upgrade path first, then schedule the quorum change as a separate step.
Customizations and tooling that reference controller.quorum.votersScripts, health checks, Helm values, Ansible roles and monitoring rules that hard-code the voter list must be found and changed. Missed references often surface only after a controller is added or removed.Inventory every file and dashboard that names controller IDs or endpoints, and test each change in a non-production cluster.
Automation and configuration managementConfiguration drift across hosts makes it harder to confirm that every node uses the intended quorum settings. Automation can also re-render the old static voter list and undo changes.Pause or update configuration management before the change, then reconcile the rendered files against the running cluster.
Network and TLS setup for controller listenersController listeners must be reachable from every controller that joins, and any TLS configuration must cover the new nodes. Firewall rules, certificate SANs and DNS entries are common delays.Verify connectivity and certificates on each new controller endpoint before running add-controller.
Size of the metadata logA larger metadata log means new controllers take longer to catch up before they can be added. Check the replication state and lag before each step.Check the metadata log status with the quorum tooling before each add or remove, and do not proceed while a new controller is still catching up.

Questions teams ask before changing controllers

What does a controller quorum change cost, and what drives the price?

DeployOpen does not publish a price list, and a quote follows from the written scope agreed before work starts. Apache Kafka is licensed under Apache-2.0 with no paid edition from the project, so the cost sits in people, rehearsal environments and change support. The one-time work covers reviewing the current controller.quorum.voters setup, checking kraft.version, writing the change runbook, rehearsing in a non-production cluster, executing the controller changes in your window and verifying the result afterward. A recurring cost appears only if you add a retainer for upgrades or support. The main drivers are the number of clusters, the number of controllers to add and remove, the number of environments, your change approval requirements and the size of the client estate that needs checking. Ask for line items for planning, rehearsal, execution, verification and handover so you can compare them with your own team's time.

How long does moving to a dynamic quorum take?

The written scope should give the timeline, and it should be treated as something to confirm rather than assume. The hands-on steps for each controller are short, but elapsed time depends on how long each new controller takes to catch up with the metadata log. That depends on log size, disk speed and the network between nodes and sites. Other drivers include the number of controllers to change, whether you rehearse in staging first, your change calendar and approval cycle, and whether the Kafka version needs an upgrade before the quorum switch. Multi-site layouts add time because each controller placement needs its own network and latency checks. Ask the partner to break the timeline into phases with entry and exit checks, and ask which of those phases your change board must approve.

What must our team provide before the controller change starts?

The team needs to provide access and configuration details so the change is planned against your actual cluster. Expect to supply:

  • Administrative access to the controller and broker hosts, and the Kafka version running on each.
  • The current server.properties files for every controller and broker, including controller.quorum.voters and listener settings.
  • Output from kafka-features.sh describe and kafka-metadata-quorum.sh describe taken while the cluster is healthy.
  • The Java runtime on each node, since brokers require Java 17 from Kafka 4.0 onward.
  • A non-production cluster or rehearsal window, along with your change calendar and named approvers.
  • Named owners for the client teams that must be warned about restarts or endpoint changes.

Missing any of these items usually moves work from planning into discovery during the change window, which is the costliest place to find problems.

Can our own platform team make this change without a partner?

Many teams can, provided they have someone who has operated KRaft in production and a tested rollback plan. The documented sequence is short: confirm the feature level with kafka-features.sh describe, add new controllers with kafka-metadata-quorum.sh add-controller after they have caught up, and remove controllers with remove-controller before shutting them down. The risks lie in the details, such as removing the wrong node, dropping below a safe number of voters, mismatched node IDs or configuration errors on a new controller. A partner typically adds a written runbook, a rehearsal in a non-production cluster, a second pair of hands during execution and written evidence for your change record. Decide based on your team's Kafka experience, how many people can be on call during the window and whether a failed step can wait until business hours.

Is a rolling upgrade from recent Kafka releases supported?

Yes. The Kafka time-based release plan says rolling upgrades are supported from each release in the past year, which is the last three releases, to the latest version. The supported releases listed on the downloads page are 4.3.1, 4.2.2 and 4.1.2, so a cluster on one of those can move to the current release by rolling upgrade. Confirm the exact path for your starting version in the upgrade notes. Clusters that are KRaft-based but older than 3.3.x are advised to move to 3.9.x first, and ZooKeeper-based clusters must migrate to KRaft before moving to 4.x. The move to a dynamic quorum is a separate step. Plan it after the cluster is on a stable, supported version and after you have checked the upgrade notes for that version.

What happens if adding a new controller fails partway through?

The existing controllers keep serving the quorum, so a failed add should not by itself stop production writes, but confirm this against the documentation for your version before you rely on it. Do not run remove-controller on existing controllers to clear the failure. Instead, check the quorum status with kafka-metadata-quorum.sh describe to see whether the new controller has caught up. Then review the new node's logs for causes such as network reachability, disk space, a wrong node ID, a listener or TLS misconfiguration or a mismatched directory. Fix the cause and retry add-controller only after the new node is ready. Remove a controller only when the quorum status shows the remaining voters are healthy. Ask the partner to document this decision path in the runbook before the change window opens. The KRaft operations documentation is the reference for the commands.

Will producers and consumers see downtime during the switch?

The goal is no client outage, but whether clients notice depends on the order of steps, your network and your client settings. Controller changes do not usually touch client traffic directly, yet a few conditions can cause visible impact. Removing too many voters can break the quorum, so the number of controllers in each step matters. Configuration changes that need a restart can move leadership and briefly disrupt partitions. Clients whose bootstrap lists point only to retired nodes will fail to reconnect, and DNS or load balancer changes can take longer to propagate than expected. Ask for a rehearsal that records producer and consumer behavior during each step, and compare the results against the client-side retry and timeout settings your applications use.

How DeployOpen scopes and delivers a quorum migration

DeployOpen treats a move from a static to a dynamic controller quorum as a fixed-scope change to a live metadata layer, planned in writing before any controller is touched. The work covers the feature check, the upgrade sequence, controller replacement and the runbook your operators will use afterwards.

How we scope

  • We confirm the current kraft.version level and the controller.quorum.voters list on every controller before work starts.
  • The written scope names each controller host, its storage path, its network path and the change window agreed with your team.
  • We record rollback limits, the finalization decision and who approves each step, so the boundaries are clear before the first change.

How we build

  • We rehearse the feature upgrade and controller additions on a non-production cluster that mirrors your topology and configuration.
  • We add new controllers with kafka-metadata-quorum only after each one has caught up, and we check quorum status after every change.
  • We test removal of each retiring controller before shutdown and confirm the broker and client connection strings no longer depend on the old voter list.

How we hand over

  • You receive a written record of the versions, feature levels, controller IDs and configuration files as they stand at completion.
  • The runbook covers add-controller, remove-controller, quorum status checks and the steps to take if a controller fails to catch up.
  • Your operators walk through the procedure with us, and the runbook is kept in your own repository and change system.

How we support

  • Ongoing support, upgrades and continuous testing are available as retainers for clusters that need help after the migration.
  • Retainer scope for controller changes, Kafka upgrades and periodic quorum health reviews is agreed in writing with your team.
  • Questions about future 4.x upgrades that depend on the dynamic quorum are scoped as separate written work before they begin.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.