How to Install Apache Kafka on Ubuntu 26.04 LTS (KRaft Mode, No ZooKeeper)

how to install apache Kafka on Ubuntu 26.04

Table of Contents

  1. What is Apache Kafka and When Should You Use It
  2. KRaft Mode: Why ZooKeeper is No Longer Needed
  3. Prerequisites
  4. Step 1: Install Java 21
  5. Step 2: Create a Dedicated Kafka User
  6. Step 3: Download and Install Apache Kafka 4.2
  7. Step 4: Configure Kafka in KRaft Mode
  8. Step 5: Format the Storage Directory
  9. Step 6: Create a systemd Service
  10. Step 7: Configure UFW Firewall
  11. Step 8: Test with Producer and Consumer
  12. Step 9: Basic Topic Management
  13. Production Configuration Tips
  14. Kafka vs RabbitMQ: When to Use Which
  15. Common Issues and Quick Fixes
  16. Next Steps

    Real-time data is everywhere — microservices firing events, IoT sensors streaming telemetry, user actions triggering notifications, logs flowing from dozens of servers simultaneously. Managing all of that reliably, at scale, without building a custom integration between every producer and every consumer, is exactly the problem Apache Kafka was built to solve.

    This guide walks through installing Apache Kafka 4.2 on Ubuntu 26.04 LTS (Resolute Raccoon) using KRaft mode — the modern deployment approach that removes the ZooKeeper dependency entirely. If you’ve seen older Kafka tutorials that tell you to start ZooKeeper first, those are outdated: starting with Kafka 4.0, ZooKeeper support was completely removed.

    What is Apache Kafka and When Should You Use It

    Apache Kafka is a distributed event streaming platform — a durable, ordered log that producers write to and consumers read from. Unlike a traditional message queue where a message disappears after being consumed, Kafka retains events for a configurable period, allowing multiple independent consumers to read the same stream at their own pace.

    Real-world use cases where Kafka fits well:

    • Microservice decoupling — Service A publishes an event; Services B, C, and D each consume it independently, without A knowing or caring who’s listening.
    • Log and metrics aggregation — Centralize logs from dozens of services into one stream, then fan out to Elasticsearch, S3, and a monitoring stack simultaneously.
    • Real-time analytics — Process a stream of user events, transactions, or sensor readings as they happen rather than batching overnight.
    • Event sourcing — Store every state change as an immutable event, with the ability to replay history to rebuild application state.
    • Change Data Capture (CDC) — Stream database changes (inserts, updates, deletes) to downstream systems in real time.

    Kafka is not the right tool for every messaging need. If you’re sending a task to exactly one worker and want it acknowledged once, a simpler queue (Redis Streams, RabbitMQ, or even PostgreSQL LISTEN/NOTIFY) is probably sufficient and far easier to operate. Kafka earns its complexity at scale.

    KRaft Mode: Why ZooKeeper is No Longer Needed

    Before Kafka 3.x, every Kafka deployment required a separate Apache ZooKeeper cluster to manage broker metadata, leader elections, and cluster state. This meant running and maintaining two distributed systems for every Kafka deployment — doubling the operational complexity.

    KRaft (Kafka Raft Metadata mode) replaces ZooKeeper by moving cluster metadata management directly into Kafka itself, using the Raft consensus algorithm. The result:

    • Simpler deployment — one system to install, configure, monitor, and upgrade instead of two.
    • Faster startup and failover — Kafka no longer needs to synchronize with an external ZooKeeper cluster.
    • Better scalability — ZooKeeper had practical limits on the number of partitions it could track; KRaft removes those limits.
    • Kafka 4.0+: ZooKeeper support is completely removed. There is no option to use ZooKeeper with Kafka 4.x — KRaft is the only deployment mode.

    Prerequisites

    • Ubuntu 26.04 LTS (Resolute Raccoon) — fresh install or existing server
    • Minimum 4GB RAM (8GB+ recommended for production workloads)
    • At least 2 CPU cores
    • sudo access
    • Internet access to download packages

    Step 1: Install Java 21

    Kafka 4.x requires Java 17 or higher. Java 21 LTS is the recommended choice for Ubuntu 26.04 — it’s the current Long-Term Support release and ships cleanly from Ubuntu’s default repository:

    sudo apt update
    sudo apt install -y openjdk-21-jdk-headless

    Verify the installation:

    java -version

    Expected output:

    openjdk version "21.x.x" ...
    OpenJDK Runtime Environment (build 21.x.x+...)

    Set JAVA_HOME so Kafka can find it:

    echo 'export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64' | \
      sudo tee /etc/profile.d/java.sh
    source /etc/profile.d/java.sh

    Confirm:

    echo $JAVA_HOME
    # /usr/lib/jvm/java-21-openjdk-amd64

    Step 2: Create a Dedicated Kafka User

    Running Kafka as root is a security risk. Create a dedicated system user with no login access:

    sudo useradd -r -m -U -d /opt/kafka -s /bin/false kafka

    This follows the same principle of least privilege covered in our Docker Container Security Best Practices guide — services should run with the minimum access they need, never as root.

    Step 3: Download and Install Apache Kafka 4.2

    Set the version variables, then download and extract to /opt/kafka:

    KAFKA_VERSION="4.2.0"
    SCALA_VERSION="2.13"
    
    wget https://downloads.apache.org/kafka/${KAFKA_VERSION}/kafka_${SCALA_VERSION}-${KAFKA_VERSION}.tgz \
      -O /tmp/kafka.tgz
    
    sudo tar -xzf /tmp/kafka.tgz -C /opt/kafka --strip-components=1
    sudo chown -R kafka:kafka /opt/kafka

    Verify the extraction:

    ls /opt/kafka
    # bin  config  libs  LICENSE  licenses  NOTICE  site-docs

    Create a dedicated data directory for Kafka logs (separate from the application directory — good practice for disk management and backups):

    sudo mkdir -p /var/lib/kafka/data
    sudo chown -R kafka:kafka /var/lib/kafka

    Step 4: Configure Kafka in KRaft Mode

    KRaft mode uses a single unified configuration file (server.properties) where the broker also acts as the cluster controller. Edit the KRaft server configuration:

    sudo nano /opt/kafka/config/kraft/server.properties

    Key settings to review and update:

    # The role this node plays in the cluster
    # "broker,controller" = combined mode (suitable for single-node and small clusters)
    process.roles=broker,controller
    
    # Unique ID for this broker — change if running multiple brokers
    node.id=1
    
    # Controller quorum voters — format: node.id@host:port
    # For single node: use the same node
    controller.quorum.voters=1@localhost:9093
    
    # Listeners — what addresses Kafka binds to
    listeners=PLAINTEXT://localhost:9092,CONTROLLER://localhost:9093
    
    # The address clients use to reach the broker
    # Change 'localhost' to your server's actual IP or hostname if remote clients need access
    advertised.listeners=PLAINTEXT://localhost:9092
    
    # Listener used for inter-broker and controller communication
    inter.broker.listener.name=PLAINTEXT
    controller.listener.names=CONTROLLER
    
    # Log directory — where Kafka stores event data
    log.dirs=/var/lib/kafka/data
    
    # Number of partitions for auto-created topics
    num.partitions=3
    
    # Log retention (how long to keep events)
    log.retention.hours=168        # 7 days
    log.retention.bytes=1073741824 # 1GB per partition
    
    # Replication factor for internal topics (keep at 1 for single-node)
    offsets.topic.replication.factor=1
    transaction.state.log.replication.factor=1
    transaction.state.log.min.isr=1

    Remote access note: If clients will connect from other machines, replace localhost in advertised.listeners with your server’s actual IP address or DNS hostname. Kafka clients use the advertised address to establish connections — using localhost here will cause remote clients to fail even if they can reach the server on port 9092.

    Step 5: Format the Storage Directory

    Before first startup, Kafka’s storage directory must be initialized with a unique cluster ID. This is a one-time operation:

    # Generate a cluster ID
    CLUSTER_ID=$(/opt/kafka/bin/kafka-storage.sh random-uuid)
    echo "Cluster ID: $CLUSTER_ID"
    
    # Format the storage directory with that cluster ID
    sudo -u kafka /opt/kafka/bin/kafka-storage.sh format \
      -t $CLUSTER_ID \
      -c /opt/kafka/config/kraft/server.properties

    Expected output:

    Formatting /var/lib/kafka/data with metadata.version X.X-IV...

    This step replaces the old ZooKeeper initialization — there’s no zookeeper-server-start.sh to run first.

    Step 6: Create a systemd Service

    Create a systemd unit file so Kafka starts automatically on boot and can be managed with systemctl:

    sudo nano /etc/systemd/system/kafka.service
    [Unit]
    Description=Apache Kafka Server (KRaft Mode)
    Documentation=https://kafka.apache.org/documentation/
    After=network.target
    
    [Service]
    Type=simple
    User=kafka
    Group=kafka
    Environment="JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64"
    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
    RestartSec=10
    LimitNOFILE=65536
    StandardOutput=journal
    StandardError=journal
    SyslogIdentifier=kafka
    
    [Install]
    WantedBy=multi-user.target

    Enable and start the service:

    sudo systemctl daemon-reload
    sudo systemctl enable kafka
    sudo systemctl start kafka

    Check the status:

    sudo systemctl status kafka

    Expected output:

    ● kafka.service - Apache Kafka Server (KRaft Mode)
         Loaded: loaded (/etc/systemd/system/kafka.service; enabled)
         Active: active (running) since ...

    View live logs:

    sudo journalctl -u kafka -f

    Step 7: Configure UFW Firewall

    If UFW is enabled, open the ports Kafka needs:

    # Port 9092 — Kafka broker (clients connect here)
    sudo ufw allow 9092/tcp comment "Kafka broker"
    
    # Port 9093 — Kafka controller (internal KRaft communication)
    # Only needed if running multi-node — restrict to internal network for single-node
    sudo ufw allow from 10.0.0.0/8 to any port 9093 comment "Kafka KRaft controller"
    
    sudo ufw reload
    sudo ufw status

    Security note: Port 9092 should only be open to trusted clients, not the entire internet. If your Kafka broker is internet-facing, restrict port 9092 to specific IP ranges or put it behind a VPN.

    Step 8: Test with Producer and Consumer

    Open two separate terminal sessions for this test — one for the producer, one for the consumer.

    First: Create a test topic

    sudo -u kafka /opt/kafka/bin/kafka-topics.sh \
      --create \
      --topic test-events \
      --partitions 3 \
      --replication-factor 1 \
      --bootstrap-server localhost:9092

    Expected output:

    Created topic test-events.

    Terminal 1: Start a producer and send messages

    sudo -u kafka /opt/kafka/bin/kafka-console-producer.sh \
      --topic test-events \
      --bootstrap-server localhost:9092

    Type a few messages and press Enter after each:

    > Hello from Kafka on Ubuntu 26.04
    > This is a test message
    > Apache Kafka 4.2 KRaft mode works!

    Press Ctrl+C to exit.

    Terminal 2: Start a consumer and read messages

    sudo -u kafka /opt/kafka/bin/kafka-console-consumer.sh \
      --topic test-events \
      --from-beginning \
      --bootstrap-server localhost:9092

    You should see the messages you typed appear immediately. Press Ctrl+C to exit.

    --from-beginning tells the consumer to read from the earliest available offset — not just new messages. This illustrates one of Kafka’s key properties: consumers can replay historical events, not just receive new ones.

    Step 9: Basic Topic Management

    # List all topics
    sudo -u kafka /opt/kafka/bin/kafka-topics.sh \
      --list \
      --bootstrap-server localhost:9092
    
    # Describe a topic (partitions, replicas, leader)
    sudo -u kafka /opt/kafka/bin/kafka-topics.sh \
      --describe \
      --topic test-events \
      --bootstrap-server localhost:9092
    
    # Increase partition count (partitions can only be increased, never decreased)
    sudo -u kafka /opt/kafka/bin/kafka-topics.sh \
      --alter \
      --topic test-events \
      --partitions 6 \
      --bootstrap-server localhost:9092
    
    # Delete a topic
    sudo -u kafka /opt/kafka/bin/kafka-topics.sh \
      --delete \
      --topic test-events \
      --bootstrap-server localhost:9092
    
    # List consumer groups
    sudo -u kafka /opt/kafka/bin/kafka-consumer-groups.sh \
      --list \
      --bootstrap-server localhost:9092
    
    # Check consumer group lag (how far behind a consumer is)
    sudo -u kafka /opt/kafka/bin/kafka-consumer-groups.sh \
      --describe \
      --group my-consumer-group \
      --bootstrap-server localhost:9092

    Consumer group lag (last command) is one of the most important operational metrics in a Kafka deployment — it tells you how many messages a consumer is behind. Integrating this metric into your Prometheus + Grafana monitoring stack via kafka-exporter or JMX exporter gives you real-time visibility into consumer health.

    Production Configuration Tips

    A single-node Kafka installation on Ubuntu is sufficient for development and low-traffic production workloads. For anything that needs to scale or survive a broker failure, here are the key settings to revisit:

    Memory tuning:
    Kafka’s JVM heap is set to 1GB by default. For production, adjust in /opt/kafka/bin/kafka-server-start.sh:

    export KAFKA_HEAP_OPTS="-Xmx4G -Xms4G"

    Set both -Xmx and -Xms to the same value to prevent JVM heap resizing pauses.

    Log retention by size, not just time:

    # Retain logs for 7 days OR until they exceed 10GB per partition, whichever comes first
    log.retention.hours=168
    log.retention.bytes=10737418240

    Auto topic creation in production:

    # Disable auto-creation — require topics to be created explicitly
    auto.create.topics.enable=false

    Auto-created topics use default partition/replication settings, which are rarely correct for specific use cases. Disable this and create topics explicitly with the right parameters.

    Replication factor for multi-broker clusters:

    # With 3 brokers, use replication factor 3 and min ISR 2
    # (data survives loss of any 1 broker, writes require 2 brokers to acknowledge)
    default.replication.factor=3
    min.insync.replicas=2

    Regular backups:
    The /var/lib/kafka/data directory contains all event data. Back it up the same way you’d back up any other data volume — a scheduled snapshot with retention policy, tested restore process, stored separately from the host. The scripted approach from our Redis with Docker Compose guide applies directly to the Kafka data directory.

    Kafka vs RabbitMQ: When to Use Which

    A common question when introducing a message broker for the first time:

    Apache KafkaRabbitMQ
    ModelDistributed log (pull-based)Message queue (push-based)
    Message retentionConfigurable period (days/weeks)Until acknowledged (usually)
    ThroughputMillions of messages/secondTens to hundreds of thousands/second
    Consumer patternEach consumer group reads the full log independentlyOne consumer per message (competing consumers)
    ReplayYes — consumers can re-read past eventsNo — once consumed, message is gone
    Ordering guaranteePer partitionPer queue
    Operational complexityHigherLower
    Best forEvent streaming, log aggregation, CDC, high throughputTask queues, RPC, point-to-point messaging, simpler use cases

    Choose Kafka when: You need multiple consumers to independently process the same events, you need to replay history, or you need to handle very high throughput (millions of events/second).

    Choose RabbitMQ when: You’re building a task queue where each task should be processed exactly once by exactly one worker, the message volume is moderate, and you want simpler operations.

    Common Issues and Quick Fixes

    SymptomLikely CauseFix
    kafka.service fails to startStorage not formatted, or wrong JAVA_HOMERun kafka-storage.sh format first; verify java -version works as kafka user
    Connection refused on port 9092Kafka not running, or listener bound to wrong addressCheck systemctl status kafka; verify listeners in server.properties
    Remote clients can’t connectadvertised.listeners set to localhostChange advertised.listeners to the server’s actual IP or hostname
    Leader not available on topic createKafka still initializing after startWait 10-15 seconds after Kafka starts before creating topics
    Consumer reads no messagesConsumer started after producer, no --from-beginning flagAdd --from-beginning flag to read historical messages
    OutOfMemoryError in logsJVM heap too small for the workloadIncrease -Xmx and -Xms in kafka-server-start.sh
    High consumer group lagConsumer too slow or too few consumer instancesScale consumer instances; check processing logic for bottlenecks

    Conclusion

    With Kafka running on Ubuntu 26.04, the natural next steps are:

    • Add monitoring — deploy kafka-exporter alongside your Prometheus + Grafana stack to track consumer group lag, message throughput, and broker health in real time.
    • Containerize it — for teams already running Docker Compose stacks, Kafka has an official Docker image and can be added to an existing Compose setup using the same KRaft configuration.
    • Explore Kafka Streams or Apache Flink — for in-stream processing (filtering, aggregating, joining streams) without writing a separate consumer application.
    • Scale to multi-broker — repeat the installation on additional nodes, adjust broker.id and controller.quorum.voters, and distribute your topics’ partitions across the cluster for fault tolerance and horizontal throughput scaling.
    (Visited 1 times, 1 visits today)

    You may also like