---
title: "Deploy Apache Kafka | Open Source Event Streaming (KRaft, no ZooKeeper)"
description: "Kafka in KRaft mode with a web console — no ZooKeeper, data on a volume"
category: "Queues"
url: https://railway.com/deploy/apache-kafka
---

# Deploy Apache Kafka | Open Source Event Streaming (KRaft, no ZooKeeper)

Kafka in KRaft mode with a web console — no ZooKeeper, data on a volume

**[Deploy Apache Kafka | Open Source Event Streaming (KRaft, no ZooKeeper) on Railway](https://railway.com/template/apache-kafka)**

- **Creator:** katsuba.dev
- **Category:** Queues
- **Total deploys:** 1

## Template content

### Kafka-UI https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/kafka.svg

- **Image:** kafbat/kafka-ui:v1.5.0
- **Health check:** /actuator/health
- **Public domain:** Yes

### Kafka https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/kafka.svg

- **Image:** apache/kafka:4.3.1

## Documentation

# Deploy and Host Apache Kafka on Railway

Apache Kafka is the distributed log that most event-driven systems are built on: producers append records to topics, consumers read them at their own pace, and nothing is lost in between. This template runs it in KRaft mode — no ZooKeeper — with a web console in front of it.

## About Hosting Apache Kafka

This template runs the official `apache/kafka` image on a pinned stable tag, with partitions and the metadata log on a persistent Railway volume, plus [kafbat's Kafka UI](https://ui.docs.kafbat.io/) as a management console. Nothing is rebuilt or forked, so upstream releases and upstream security fixes are what you get.

Two choices here are worth stating plainly, because they are the ones that go wrong elsewhere.

The image is Apache's own, not Bitnami's. Bitnami removed versioned tags from the free Docker Hub tier in 2025, and templates pinned to them stopped deploying — a template that cannot pull its image is worse than no template.

The broker is reachable **only inside your Railway project**, at `kafka.railway.internal:9092`. Kafka has no authentication over PLAINTEXT, so a broker on a public TCP proxy is a cluster anyone can produce to, consume from and delete topics in. Doing external access properly means SASL, certificates and a client configuration this template cannot fill in for you. The console is the one thing on a public domain, and it sits behind a login form with a generated password.

## Common Use Cases

- **Decoupling services**: Let services publish events instead of calling each other, so a slow or restarting consumer never takes the producer down with it.
- **Durable job and event queues**: Retain a replayable log rather than a queue that forgets — reprocess from any offset after a bug fix.
- **Streaming pipelines and CDC**: Feed change data, clickstreams or telemetry into analytics, search or a warehouse, with backpressure handled by the log itself.

## Dependencies for Apache Kafka Hosting

### Deployment Dependencies

- [Apache Kafka 4.3](https://kafka.apache.org/documentation/) in KRaft mode — no ZooKeeper
- [Kafbat Kafka UI 1.5](https://ui.docs.kafbat.io/) — topic browser, message viewer, consumer group inspector
- A Railway volume mounted at `/var/lib/kafka/data`
- Producers and consumers running in the same Railway project

### Implementation Details

`RAILWAY_RUN_UID=0` runs the broker as root. The `apache/kafka` image declares `USER appuser`, and a non-root process cannot take ownership of a freshly attached Railway volume — without this the broker starts and immediately fails to write its log directory. This is Railway's documented answer for images that run as a fixed non-root user and need a volume.

`KAFKA_LOG_DIRS` points at `/var/lib/kafka/data/logs`, one level below the mount point rather than at it. Railway creates a `lost+found` entry at the root of every volume, and Kafka treats every entry inside a log directory as a partition directory, so pointing it at the mount root aborts the boot with `Found directory /var/lib/kafka/data/lost+found, 'lost+found' is not in the form of topic-partition`. The image's own default, `/tmp/kraft-combined-logs`, is worse in a quieter way: it is the ephemeral container layer, so every topic would vanish on the next deploy without an error anywhere.

`KAFKA_LISTENERS` leaves the host empty — `PLAINTEXT://:9092`. The JVM binds a dual-stack wildcard socket that way, which is what Railway's IPv6-only private network requires. Writing `0.0.0.0` instead makes `kafka.railway.internal` unreachable to every other service in the project.

`KAFKA_ADVERTISED_LISTENERS` uses `${{RAILWAY_PRIVATE_DOMAIN}}`. Bootstrap returns the advertised address, and clients reconnect to that, not to the host they dialled. Left at the default, the broker advertises its container hostname: the first connection succeeds and every subsequent one times out — a failure that looks like a network problem and is not.

`CLUSTER_ID` is generated once and written into the volume on the first boot. The image's entrypoint exits immediately if it is unset, and changing it later describes a cluster that is not the one on disk. Every replication factor is pinned to `1` because there is one broker; left at multi-broker defaults, the internal offsets topic is never created and the first consumer group hangs forever.

The broker carries no healthcheck, deliberately. Railway keeps the previous container serving until a new one reports healthy, while Kafka holds an exclusive lock on its log directory — with both on the same volume that is a deadlock, and every redeploy after the first would fail. The console does have one, at `/actuator/health`, because it is stateless.

## Why Deploy Apache Kafka on Railway?

Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.

By deploying Apache Kafka on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.

**Resources**: 1 GB RAM for the broker and around 512 MB for the console is a comfortable start, with a 1 GB volume that can be grown later without downtime. The heap is capped at 512 MB — raise `KAFKA_HEAP_OPTS` and the service memory together, not one without the other. Retention is the default seven days; disk is what runs out first on a busy topic, so set `retention.ms` per topic before you scale the volume.


## Similar templates

- [smoothmq](https://railway.com/deploy/AJv-64) — A drop-in replacement for AWS SQS
- [Kafka UI](https://railway.com/deploy/kafka-ui) — Kafbat UI — Open-source web UI to monitor and manage Apache Kafka clusters
- [Hatchet Lite](https://railway.com/deploy/hatchet-lite) — Hatchet Lite with postgres

Open this page in a browser: https://railway.com/deploy/apache-kafka
