Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive · DevOps

Apache Kafka 3.7: Installing a KRaft Broker Without ZooKeeper

Install Kafka 3.7 in KRaft mode on Ubuntu with a systemd service, a safe data directory, a first topic, and the path from a single node to a production quorum.

3 min read

  • Apache Kafka 3.7
  • KRaft
  • OpenJDK 17
  • Ubuntu 22.04 LTS
  • systemd

Apache Kafka is a distributed log used for event streaming. Older guides start ZooKeeper first. Modern Kafka uses KRaft, where the brokers manage their own metadata with a Raft quorum, so there is nothing else to run.

Versions in use on this date. Kafka 3.7.0 was released on 27 February 2024. KRaft has been production-ready for new clusters since 3.3, and migration of existing ZooKeeper clusters became production-ready in 3.6. ZooKeeper mode is deprecated and is planned for removal in Kafka 4.0. Kafka 3.7 also publishes an official Docker image (apache/kafka). Check the Apache downloads page for any 3.7.x patch release before installing.

Target architecture

flowchart TB
  subgraph DEV[Learning setup - one node]
    N1[Kafka node<br/>roles broker and controller]
  end
  subgraph PROD[Production shape]
    C1[Controller 1] --- C2[Controller 2]
    C2 --- C3[Controller 3]
    B1[Broker 1] --> C1
    B2[Broker 2] --> C2
    B3[Broker 3] --> C3
  end
  P[Producers] -->|9092 or TLS 9093| B1
  CG[Consumer groups] --> B2
  B1 --- B2
  B2 --- B3
  B1 --> D1[(Dedicated log disk)]

A single node with process.roles=broker,controller is fine for learning. In production run three controllers (an odd number, so the quorum survives one failure) and at least three brokers, on separate hosts or availability zones. Small clusters sometimes keep controller and broker roles combined on three nodes.

Install

sudo apt update && sudo apt install -y openjdk-17-jre-headless
sudo useradd -r -m -d /opt/kafka -s /usr/sbin/nologin kafka
cd /tmp
curl -LO https://archive.apache.org/dist/kafka/3.7.0/kafka_2.13-3.7.0.tgz
curl -LO https://archive.apache.org/dist/kafka/3.7.0/kafka_2.13-3.7.0.tgz.sha512
sha512sum kafka_2.13-3.7.0.tgz     # compare with the published checksum
sudo tar -xzf kafka_2.13-3.7.0.tgz -C /opt/kafka --strip-components=1
sudo mkdir -p /var/lib/kafka/data && sudo chown -R kafka:kafka /opt/kafka /var/lib/kafka

Configure

The sample config/kraft/server.properties writes data to /tmp/kraft-combined-logs, which is deleted on reboot. Always change log.dirs. Create /opt/kafka/config/kraft/server.properties for a single node:

process.roles=broker,controller
node.id=1
controller.quorum.voters=1@localhost:9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
advertised.listeners=PLAINTEXT://kafka1.example.com:9092
controller.listener.names=CONTROLLER
inter.broker.listener.name=PLAINTEXT
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
log.dirs=/var/lib/kafka/data
num.partitions=3
default.replication.factor=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
log.retention.hours=168

advertised.listeners is the address clients are told to reconnect to; a wrong value is the most common reason that a client connects, then hangs. For a three-node cluster, set a unique node.id on each, list all voters (1@k1:9093,2@k2:9093,3@k3:9093) and raise replication factors to 3 with min.insync.replicas=2.

Format storage once with a cluster ID (the same ID on every node):

CLUSTER_ID=$(/opt/kafka/bin/kafka-storage.sh random-uuid)
sudo -u kafka /opt/kafka/bin/kafka-storage.sh format -t $CLUSTER_ID -c /opt/kafka/config/kraft/server.properties

systemd service

# /etc/systemd/system/kafka.service
[Unit]
Description=Apache Kafka (KRaft)
After=network.target

[Service]
User=kafka
Environment="KAFKA_HEAP_OPTS=-Xms1g -Xmx1g"
ExecStart=/opt/kafka/bin/kafka-server-start.sh /opt/kafka/config/kraft/server.properties
ExecStop=/opt/kafka/bin/kafka-server-stop.sh
Restart=on-failure
LimitNOFILE=100000

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload && sudo systemctl enable --now kafka
sudo journalctl -u kafka -f

Keep the heap modest (Kafka relies on the OS page cache, so leave most RAM free) and put log.dirs on a dedicated XFS or ext4 disk.

First topic and a smoke test

cd /opt/kafka
bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic orders --partitions 3 --replication-factor 1
bin/kafka-topics.sh --bootstrap-server localhost:9092 --describe --topic orders
echo "order-1" | bin/kafka-console-producer.sh --bootstrap-server localhost:9092 --topic orders
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic orders --from-beginning --max-messages 1

Security before production

PLAINTEXT is for labs. Use TLS (SSL or SASL_SSL listeners) with SASL/SCRAM or mTLS for authentication, and enable ACLs with authorizer.class.name=org.apache.kafka.metadata.authorizer.StandardAuthorizer (the KRaft authorizer). Firewall 9092 and 9093 to known networks.

Failure modes

  • Fails with “Invalid cluster.id”: the data directory was formatted with a different cluster ID; reformat only if the data is disposable.
  • Quorum never forms: voters list differs between nodes, or port 9093 is blocked.
  • Disk fills: set retention by time and size (log.retention.bytes) and alert at 70% usage.
  • Migrating from ZooKeeper: follow the official migration guide node by node; do not reuse a ZooKeeper data directory for KRaft.