skip to content

Explain the process.roles, node.id, and controller.quorum.voters configuration properties in KRaft. What does each control?

level: middleimportance: must knowfreq 70%

answer

  1. process.roles = broker | controller | broker,controller
  2. node.id = unique int (replaces broker.id)
  3. controller.quorum.voters = id@host:port list
  4. combined = dev only; dedicated controllers for prod
  5. KIP-853 dynamic quorum + bootstrap.servers

basics

~20 s

process.roles sets whether a node is a broker, controller, or both (combined). node.id is the node's unique integer ID in the cluster. controller.quorum.voters lists the controller nodes (id@host:port) that form the metadata Raft quorum, so every node knows who the controllers are.

solid answer

~50 s

These three configs define a KRaft node's identity and role. process.roles takes broker, controller, or broker,controller (combined mode); it determines whether the process serves client data, participates in the metadata Raft quorum, or both. node.id is a unique integer identifying this node across the whole cluster — it replaces the old broker.id and is also the ID used in the quorum voter list. controller.quorum.voters is a static comma-separated list of {id}@{host}:{port} entries naming every controller voter, so all nodes can find the controllers and the controllers can form their Raft group. Combined mode (a single process is both broker and controller) is convenient for development and small clusters but discouraged for production, where you run dedicated controller nodes (typically 3) separate from brokers for isolation and predictable failover. In newer Kafka, dynamic quorum membership (KIP-853) lets you add/remove voters at runtime via controller.quorum.bootstrap.servers instead of the static voters list.

code

properties · 6 lines
properties
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@host1:9093,2@host2:9093,3@host3:9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
controller.listener.names=CONTROLLER
inter.broker.listener.name=PLAINTEXT

go deeper

for a junior

Recall that process.roles picks broker/controller/both, node.id is the unique ID, and controller.quorum.voters lists the controllers.

for a middle

Explain each precisely, the id@host:port format, combined vs dedicated, and bootstrapping with kafka-storage format.

for a senior

Discuss odd voter counts, controller listener wiring, production topology choices, and failure pitfalls.

for a principal

Reason about static vs dynamic (KIP-853) quorum membership, controller sizing/isolation trade-offs, and operational replacement of voters.

## The three identity configs When you configure a KRaft node you must answer three questions: *what does this node do?*, *who is it?*, and *who are the controllers?* These map to three properties. ### 1. `process.roles` This declares the node's **role(s)**. Valid values: - **`broker`** — the node stores topic data and serves producers/consumers, but does **not** vote in the metadata quorum. It is an observer of `__cluster_metadata`. - **`controller`** — the node participates in the metadata Raft quorum (it is a **voter**); it can become the active controller. A pure controller does **not** serve client data. - **`broker,controller`** — **combined mode**: one process plays both roles. If `process.roles` is set, the node runs in KRaft mode. (In the old days an unset `process.roles` meant ZooKeeper mode; since Kafka 4.0 ZooKeeper mode is gone.) **Combined vs dedicated**: combined mode is great for laptops, demos, and tiny clusters because you run fewer processes. For production it is discouraged — a controller competing for CPU/heap with broker data traffic makes failover and metadata latency less predictable, and isolation/security boundaries are cleaner with **dedicated controllers** (commonly 3 controller-only nodes plus N broker-only nodes). ### 2. `node.id` A **unique integer** identifying this node within the cluster. It unifies what used to be `broker.id`. The same ID is used to refer to the node in `controller.quorum.voters`. Two nodes must never share a `node.id`. For controllers, this ID is the node's identity in the Raft group. ### 3. `controller.quorum.voters` A **static list** of the controller voters in the form: ``` controller.quorum.voters=1@host1:9093,2@host2:9093,3@host3:9093 ``` Each entry is `{node.id}@{host}:{controller-listener-port}`. Every node in the cluster — brokers and controllers alike — needs this so it can locate the controllers. The controllers use it to form their Raft quorum and elect a leader. Because it is static, the membership is fixed at the listed set; you cannot casually add/remove voters. ### Related listener config Controllers expose a dedicated listener (e.g. `CONTROLLER`) named in `controller.listener.names`, and brokers connect to it. The port in the voter list is that controller listener's port (commonly 9093). ## Cluster bootstrapping Before first start you generate a cluster ID with `kafka-storage.sh random-uuid` and format each node's storage with `kafka-storage.sh format --cluster-id <id> --config server.properties`. This writes the `meta.properties` containing the `node.id` and cluster ID and initializes `__cluster_metadata`. ## Dynamic quorums (newer Kafka) The static `controller.quorum.voters` is rigid. **KIP-853 (dynamic KRaft quorums)** lets you **add and remove voters at runtime**. With it you configure **`controller.quorum.bootstrap.servers`** (a discovery endpoint list) instead of a fixed voter set, and use `kafka-metadata-quorum.sh` to add/remove controllers. This is important for operations like replacing a failed controller without a full reconfiguration. ## Common pitfalls / edge cases - Reusing a `node.id` across two nodes corrupts cluster identity. - Listing the wrong port (broker listener instead of controller listener) in `controller.quorum.voters` breaks quorum formation. - Mixing combined and dedicated controllers inconsistently, or changing a node's role after format, can require re-formatting. - The number of voters should be **odd** (typically 3 or 5) so a clear majority exists.

  • When would you choose combined mode (broker,controller) vs dedicated controllers?
    Combined for development, demos, and very small clusters to minimize processes. Dedicated controller-only nodes for production: better isolation, predictable failover, and cleaner security boundaries, since controllers do not compete with broker data traffic for resources.
  • Why should the number of controller voters be odd?
    Raft commits require a majority. An odd count (3, 5) gives an unambiguous majority and maximizes fault tolerance per node: 3 voters tolerate 1 failure, 5 tolerate 2. An even count wastes a node without improving the failure tolerance.
  • What replaces the static controller.quorum.voters for runtime membership changes?
    KIP-853 dynamic quorums: you use controller.quorum.bootstrap.servers for discovery and kafka-metadata-quorum.sh to add/remove voters at runtime, instead of a fixed static voter list.

saying these in an interview costs you the question

  • Saying node.id and broker.id are separate IDs in KRaft (node.id unifies them)
  • Recommending combined mode for production as a default
  • Putting the broker listener port (e.g. 9092) instead of the controller listener port in controller.quorum.voters
  • Claiming a pure controller node serves producer/consumer traffic
  • Using an even number of voters expecting better fault tolerance

context