Installing Apache Kafka 4.2 on Ubuntu 24.04 (WSL2) Using KRaft Mode: A Complete Step-by-Step Guide
Learn how to install Apache Kafka 4.2 in KRaft mode, understand its architecture, create topics, produce and consume messages, and troubleshoot common configuration issues—all without ZooKeeper.
🚀 Introduction
Apache Kafka has become the de facto standard for building event-driven, real-time, and high-throughput applications. Whether you’re processing millions of financial transactions, collecting application logs, streaming IoT sensor data, or connecting microservices, Kafka provides a scalable and reliable messaging platform.
Until recently, setting up Kafka required running Apache ZooKeeper alongside Kafka brokers. While powerful, ZooKeeper added operational complexity and introduced another distributed system that administrators had to manage.
Beginning with recent Kafka releases, KRaft (Kafka Raft Metadata mode) removes this dependency by allowing Kafka to manage its own metadata internally. This makes installation simpler, reduces operational overhead, and improves scalability.
In this guide, we’ll install Apache Kafka 4.2 on Ubuntu 24.04.4 LTS (WSL2), configure a single-node KRaft cluster, and walk through the complete lifecycle:
- Installing Kafka
- Understanding the Kafka architecture
- Configuring KRaft mode
- Starting the broker
- Creating topics
- Producing and consuming messages
- Troubleshooting common issues
- Understanding the purpose of each configuration parameter
Rather than simply listing commands, I’ll explain why each step is necessary so that you understand how Kafka works under the hood.
What is Apache Kafka?
Apache Kafka is a distributed event streaming platform designed to move data reliably and efficiently between applications.
Instead of applications communicating directly with each other, they communicate through Kafka.
A producing application writes messages to Kafka.
Kafka stores those messages reliably.
One or more consuming applications read those messages whenever they need them.
This decouples applications from one another, allowing them to scale independently.
For example, imagine an online shopping application.
Customer Places Order
│
▼
Order Service
│
▼
Kafka Topic
/ | \
▼ ▼ ▼
Inventory Billing Shipping
Enter fullscreen mode Exit fullscreen mode
The Order Service publishes a single event.
Inventory updates stock.
Billing processes the payment.
Shipping prepares the shipment.
Each service works independently without knowing about the others.
This is one of Kafka’s greatest strengths.
Kafka Architecture
The following diagram illustrates the overall architecture of Apache Kafka running in KRaft mode.
(Insert the cover architecture image here.)
The architecture consists of three primary components:
Producers
Producers publish messages into Kafka.
Examples include:
- Java applications
- Spring Boot microservices
- Python applications
- IoT devices
- Web applications
- Log collection systems
A producer simply sends data—it doesn’t need to know who will consume it.
Kafka Cluster
The Kafka cluster is responsible for:
- Receiving messages
- Persisting messages to disk
- Replicating data
- Managing topics
- Managing partitions
- Delivering messages to consumers
In KRaft mode, the cluster also manages its own metadata through the Controller.
Consumers
Consumers subscribe to topics and process messages.
Examples include:
- Analytics applications
- Fraud detection services
- Notification systems
- Machine Learning pipelines
- Search indexing services
Multiple consumers can read the same topic independently.
Core Kafka Concepts
Before installing Kafka, it’s helpful to understand a few important concepts.
Broker
A Broker is a Kafka server.
It receives messages from producers and stores them on disk.
A Kafka cluster may contain one broker or hundreds of brokers.
For this tutorial, we’ll use a single broker.
Topic
A Topic is a logical category used to organize messages.
Examples:
orders
payments
customer-events
application-logs
Enter fullscreen mode Exit fullscreen mode
Applications publish messages to topics.
Consumers subscribe to topics.
Partition
Each topic is divided into one or more Partitions.
Partitions allow Kafka to:
- Scale horizontally
- Process data in parallel
- Increase throughput
For a small development environment, one partition is usually sufficient.
Production systems often contain dozens or even hundreds of partitions.
Producer
A Producer writes messages to a Kafka topic.
For example:
Order Created
Payment Successful
Inventory Updated
Enter fullscreen mode Exit fullscreen mode
Each message is appended sequentially to the topic.
Consumer
A Consumer continuously reads messages from Kafka.
Consumers remember the last message they processed using Offsets, allowing them to resume processing after a restart.
What is KRaft?
Older versions of Kafka required Apache ZooKeeper to maintain cluster metadata.
ZooKeeper stored information such as:
- Broker registration
- Controller election
- Topic metadata
- Partition assignments
- Cluster configuration
While reliable, this meant administrators had to deploy and maintain two distributed systems.
Beginning with modern Kafka releases, this dependency has been removed.
Kafka now uses KRaft (Kafka Raft Metadata mode).
Instead of relying on ZooKeeper, Kafka stores metadata internally using the Raft consensus protocol.
This results in:
- Simpler deployment
- Fewer moving parts
- Lower operational overhead
- Improved scalability
- Better long-term maintainability
For new Kafka deployments, KRaft is the recommended architecture.
Installing Apache Kafka 4.2
Before installing Kafka, verify that Java 11 or later is installed.
java -version
Enter fullscreen mode Exit fullscreen mode
Download Kafka:
wget https://downloads.apache.org/kafka/4.2.0/kafka_2.13-4.2.0.tgz
Enter fullscreen mode Exit fullscreen mode
Extract the archive:
tar -xzf kafka_2.13-4.2.0.tgz
Enter fullscreen mode Exit fullscreen mode
Navigate into the installation directory:
cd kafka_2.13-4.2.0
Enter fullscreen mode Exit fullscreen mode
At this point, Kafka has been downloaded but is not yet initialized.
The next step is configuring Kafka to run in KRaft mode.
Understanding server.properties
Almost every important Kafka configuration lives inside:
config/server.properties
Enter fullscreen mode Exit fullscreen mode
Although this file contains many configuration options, only a handful are essential for a single-node KRaft installation.
process.roles=broker,controller
node.id=1
controller.quorum.voters=1@localhost:9093
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
advertised.listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093
controller.listener.names=CONTROLLER
log.dirs=/tmp/kraft-combined-logs
delete.topic.enable=true
Enter fullscreen mode Exit fullscreen mode
Let’s briefly understand what each property does.
Property Purpose process.roles Defines whether this node acts as a Broker, Controller, or both. node.id Unique identifier for the Kafka node. controller.quorum.voters Identifies which nodes participate in controller elections. listeners Specifies the network ports Kafka listens on. advertised.listeners Tells clients how they should connect to Kafka. controller.listener.names Indicates which listener is reserved for controller communication. log.dirs Directory used to store Kafka logs and metadata. delete.topic.enable Enables topic deletion.Notice that Kafka listens on two ports:
Port Purpose 9092 Producers, Consumers, Kafka Clients 9093 Internal KRaft Controller CommunicationYour Java applications, producers, consumers, and administration tools will typically connect to 9092.
The 9093 listener is reserved for Kafka’s internal controller operations.
In the next section, we’ll initialize Kafka storage, start the broker, create our first topic, and begin publishing and consuming messages.
Initializing Kafka Storage and Starting the Broker
Now that Kafka is installed and server.properties is configured, the next step is to initialize the metadata storage required by KRaft mode.
Unlike older Kafka versions that relied on ZooKeeper to maintain cluster metadata, KRaft stores metadata directly within Kafka. Before the broker can start, this storage must be initialized exactly once.
Step 1 – Generate a Cluster ID
Every Kafka cluster has a unique Cluster ID.
Generate one using:
bin/kafka-storage.sh random-uuid
Enter fullscreen mode Exit fullscreen mode
Example output:
NBPgGxgHTuuFVYzV3U6oyA
Enter fullscreen mode Exit fullscreen mode
Your value will be different.
Save this value because it will be used during the storage formatting step.
Step 2 – Format the Metadata Storage
Initialize the storage using the Cluster ID that was generated in the previous step.
bin/kafka-storage.sh format \
-t NBPgGxgHTuuFVYzV3U6oyA \
-c config/server.properties
Enter fullscreen mode Exit fullscreen mode
If everything is configured correctly, Kafka will display output similar to:
Formatting dynamic metadata voter directory /tmp/kraft-combined-logs
Enter fullscreen mode Exit fullscreen mode
This command creates the metadata required by the KRaft controller.
Important: Formatting should only be performed when creating a new Kafka cluster. Running it again on an existing cluster will erase the existing metadata.
Step 3 – Start Kafka
Start the Kafka broker using:
bin/kafka-server-start.sh config/server.properties
Enter fullscreen mode Exit fullscreen mode
Kafka starts as a foreground process.
You should begin seeing log messages indicating that the broker and controller have started successfully.
Leave this terminal running.
Understanding the Listener Ports
One question that often confuses new Kafka users is:
Why does Kafka use two ports?
Earlier we configured:
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
Enter fullscreen mode Exit fullscreen mode
These listeners serve different purposes.
Port Purpose 9092 Client communication (Producers, Consumers, Admin tools, Java applications) 9093 Internal KRaft Controller communicationThink of 9092 as the public entrance to Kafka.
Applications connect to this port to publish and consume messages.
The 9093 listener is reserved for Kafka itself. It is used internally by the controller to manage cluster metadata and coordinate the broker.
As an application developer, you will almost always connect to 9092.
Creating Your First Topic
A Topic is a logical channel used to organize messages.
Let’s create one named my-test-topic.
bin/kafka-topics.sh \
--create \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Expected output:
Created topic my-test-topic.
Enter fullscreen mode Exit fullscreen mode
Notice that we connected using localhost:9092.
The --bootstrap-server parameter specifies the Kafka broker that the client should contact.
Listing Existing Topics
To display all available topics:
bin/kafka-topics.sh \
--list \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Example output:
my-test-topic
Enter fullscreen mode Exit fullscreen mode
If your Kafka installation contains multiple topics, each one will be listed.
Describing a Topic
To display detailed information about a topic:
bin/kafka-topics.sh \
--describe \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Typical output includes:
- Topic name
- Partition count
- Replication factor
- Leader
- Replicas
- In-Sync Replicas (ISR)
Understanding these values becomes increasingly important as your Kafka cluster grows beyond a single broker.
Producing Messages
Now let’s send our first messages.
Open a second terminal and execute:
bin/kafka-console-producer.sh \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
You’ll see a prompt:
>
Enter fullscreen mode Exit fullscreen mode
Everything you type is immediately published to Kafka.
For example:
Hello Kafka!
Learning Kafka with KRaft mode.
This is my first Kafka message.
Enter fullscreen mode Exit fullscreen mode
Each line becomes a separate Kafka message.
Consuming Messages
Open a third terminal.
Run:
bin/kafka-console-consumer.sh \
--topic my-test-topic \
--from-beginning \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Example output:
Hello Kafka!
Learning Kafka with KRaft mode.
This is my first Kafka message.
Enter fullscreen mode Exit fullscreen mode
The --from-beginning option instructs Kafka to replay all existing messages in the topic.
This is useful when verifying your setup or replaying historical events.
Reading Only New Messages
If you’re only interested in messages produced after the consumer starts, simply omit the --from-beginning option.
bin/kafka-console-consumer.sh \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Now the consumer waits for new incoming messages.
As soon as a producer publishes another message, it immediately appears in the consumer terminal.
This demonstrates Kafka’s real-time event streaming capability.
How Message Flow Works
At this point, you’ve already built a complete event-driven messaging system.
The overall flow looks like this:
Producer
│
│ Publish Message
▼
+-------------------+
| Kafka |
| Topic Storage |
+-------------------+
│
│ Read Messages
▼
Consumer
Enter fullscreen mode Exit fullscreen mode
The producer and consumer never communicate directly.
Kafka sits between them, storing messages reliably and allowing multiple consumers to process the same events independently.
This decoupling is one of Kafka’s biggest architectural advantages and is widely used in microservices and distributed systems.
Verifying That Everything Works
At this stage, you should have successfully:
- Installed Kafka
- Configured KRaft mode
- Started the broker
- Created a topic
- Produced messages
- Consumed messages
You now have a fully functioning single-node Kafka environment ready for development and experimentation.
In the next section, we’ll look at common administration commands, deleting topics, troubleshooting configuration issues—including one that prevented my own Kafka broker from starting—and discuss where to go next in your Kafka learning journey.
Kafka Administration
Once your Kafka broker is running successfully, you’ll frequently perform administrative tasks such as listing topics, inspecting topic details, and deleting topics during development.
Let’s look at some of the most common commands.
Listing All Topics
To display every topic in the Kafka cluster:
bin/kafka-topics.sh \
--list \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Example output:
my-test-topic
orders
payments
application-logs
Enter fullscreen mode Exit fullscreen mode
This command is useful for verifying that topics were created successfully.
Describing a Topic
To view detailed information about a topic:
bin/kafka-topics.sh \
--describe \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Typical output contains information similar to:
Topic: my-test-topic
PartitionCount: 1
ReplicationFactor: 1
Leader: 1
Replicas: 1
Isr: 1
Enter fullscreen mode Exit fullscreen mode
Let’s briefly understand what these values mean.
Property Description PartitionCount Number of partitions belonging to the topic ReplicationFactor Number of replicas maintained for fault tolerance Leader Broker currently responsible for reads and writes Replicas Brokers containing copies of the partition ISR In-Sync Replicas currently synchronized with the leaderIn this tutorial we’re using a single-node Kafka cluster, so each value is simply 1.
Deleting a Topic
Deleting a topic is straightforward.
bin/kafka-topics.sh \
--bootstrap-server localhost:9092 \
--delete \
--topic my-test-topic
Enter fullscreen mode Exit fullscreen mode
However, topic deletion must be enabled.
Verify that the following property exists inside:
config/server.properties
Enter fullscreen mode Exit fullscreen mode
delete.topic.enable=true
Enter fullscreen mode Exit fullscreen mode
Without this configuration, Kafka ignores delete requests.
Common Kafka Commands
Create a Topic
bin/kafka-topics.sh \
--create \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
List Topics
bin/kafka-topics.sh \
--list \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Describe a Topic
bin/kafka-topics.sh \
--describe \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Produce Messages
bin/kafka-console-producer.sh \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Consume All Messages
bin/kafka-console-consumer.sh \
--topic my-test-topic \
--from-beginning \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Consume Only New Messages
bin/kafka-console-consumer.sh \
--topic my-test-topic \
--bootstrap-server localhost:9092
Enter fullscreen mode Exit fullscreen mode
Delete a Topic
bin/kafka-topics.sh \
--bootstrap-server localhost:9092 \
--delete \
--topic my-test-topic
Enter fullscreen mode Exit fullscreen mode
Real-World Issue: Why My Kafka Broker Wouldn’t Start
While writing this article, I ran into a problem that took some investigation to resolve.
After formatting the Kafka storage and attempting to start the broker, Kafka continuously displayed messages similar to:
initializeNewPublishers:
the loader is still catching up because we still don't know the high water mark yet.
Enter fullscreen mode Exit fullscreen mode
It also reported:
Unable to register the broker because the RPC got timed out before it could be sent.
Enter fullscreen mode Exit fullscreen mode
Although these messages looked alarming, they were actually symptoms rather than the root cause.
The Real Problem
After reviewing my configuration, I discovered that my server.properties file was missing the following configuration:
controller.quorum.voters=1@localhost:9093
Enter fullscreen mode Exit fullscreen mode
Without this property, Kafka’s Controller had no information about which node was responsible for participating in controller elections.
As a result:
- The Controller never became active.
- The Broker could not register.
- Kafka continued waiting indefinitely for metadata initialization.
Adding the missing configuration resolved the issue immediately.
Why This Configuration Matters
In KRaft mode, Kafka no longer relies on ZooKeeper to manage metadata.
Instead, Kafka uses an internal Controller based on the Raft consensus protocol.
The controller.quorum.voters property identifies the nodes participating in that Controller quorum.
For a single-node development environment, the configuration is simply:
controller.quorum.voters=1@localhost:9093
Enter fullscreen mode Exit fullscreen mode
In production clusters, multiple controller nodes would typically be listed.
Lessons Learned
During this installation I learned several useful lessons:
✅ KRaft mode eliminates the need for ZooKeeper.
✅ Every Kafka cluster requires a unique Cluster ID.
✅ Kafka storage must be formatted before the broker starts.
✅ Producers and consumers connect to 9092, while 9093 is reserved for internal Controller communication.
✅ The server.properties file is the heart of Kafka configuration.
✅ A missing controller.quorum.voters property prevents the Controller from becoming active.
Understanding why Kafka behaves this way makes troubleshooting much easier than simply memorizing commands.
What’s Next?
Now that Kafka is running successfully, there are many exciting topics to explore.
Some natural next steps include:
- Building a Java Kafka Producer
- Building a Java Kafka Consumer
- Understanding Partitions and Offsets
- Consumer Groups
- Kafka Streams
- Spring Boot with Kafka
- Event-Driven Microservices
- Real-time Log Processing
- Fraud Detection Pipelines
- Integrating Kafka with AI and RAG applications
Kafka is much more than a messaging system—it is a powerful event streaming platform used by organizations to build highly scalable, real-time applications.
Conclusion
In this guide, we installed Apache Kafka 4.2 on Ubuntu 24.04 (WSL2) using KRaft mode, configured a single-node broker, initialized the metadata storage, created topics, produced and consumed messages, and explored the essential administration commands used in day-to-day development.
Along the way, we also investigated and resolved a real configuration issue involving controller.quorum.voters, highlighting the importance of understanding Kafka’s internal architecture rather than simply following installation steps.
Whether you’re building microservices, processing application logs, streaming IoT data, or developing real-time analytics, Kafka provides a reliable foundation for event-driven applications.
I hope this guide helps you get started with Kafka and saves you some of the troubleshooting time that I experienced during my own setup. Happy learning!
답글 남기기