Kafka containerization
1.7K
This repository provides the Dockerfile and configuration for building a lightweight Kafka container based on Alpine Linux. The container is optimized for minimal footprint and includes Apache Kafka with Amazon Corretto OpenJDK.
server.properties before patching.KAFKA_CLUSTER_ID; formats via kafka-storage.sh.SIGTERM/SIGINT, forwards to Kafka, and propagates exit code.meta.properties already exists (i.e., persisted volume).gosu mich43l if running as root to drop privileges.The entrypoint expects:
KAFKA_BIN → Kafka binaries (kafka-server-start.sh, kafka-storage.sh)KAFKA_CONF → Config directory containing server.propertiesKAFKA_DATA → Data directory for Kafka logs (${KAFKA_DATA}/kafka-logs)The script creates
${KAFKA_DATA}/kafka-logsif missing.
If running as root, it attemptschown -R mich43l:mich43l(non-fatal if user/group aren’t present).
v2.8.2v3.6.2v3.8.0v4.1.0To build the image locally:
docker buildx build \
--tag mich43l/kafka \
--build-arg kafka_version=3.6.2 \
--build-arg scala_version=2.13 .
Kafka serverStarting zookeeper server for the Kafka broker
docker run -tid \
--name zookeeper \
--publish 2181:2181 \
--env ALLOW_ANONYMOUS_LOGIN=yes \
bitnami/zookeeper
Starting kafka server
docker run -d \
--name kafka \
-p 9092:9092 \
-e KAFKA_BROKER_ID=1 \
-e KAFKA_ZOOKEEPER_CONNECT=localhost:2181 \
-e KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 \
-e KAFKA_LOG_DIRS=/opt/kafka/data \
-v /path/to/config:/opt/kafka/config \
-v /path/to/data:/opt/kafka/data \
mich43l/kafka
The entrypoint auto-detects mode:
- If
KAFKA_ZOOKEEPER_CONNECTis set → ZooKeeper mode- Else if any of
KAFKA_NODE_ID,KAFKA_CLUSTER_ID,KAFKA_CONTROLLER_QUORUM_VOTERSis set → KRaft mode- Else → defaults to ZooKeeper (warns)
For each key, the script will uncomment, replace, or append the property in
server.properties(last one wins).
| Variable | Required | Description | Example |
|---|---|---|---|
KAFKA_CONF | ✅ | Path to config dir containing server.properties | /etc/kafka |
KAFKA_BIN | ✅ | Path to Kafka bin scripts (kafka-server-start.sh, etc.) | /opt/kafka/bin |
KAFKA_DATA | ✅ | Data root; script sets log.dirs to ${KAFKA_DATA}/kafka-logs | /var/lib/kafka |
| Variable | Mode | Default (if unset) | Notes |
|---|---|---|---|
KAFKA_LISTENERS | both | KRaft only: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093 | Appended if missing. |
KAFKA_ADVERTISED_LISTENERS | both | none | Must be reachable by clients. |
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP | both | KRaft only: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT | Sets listener.security.protocol.map. |
KAFKA_CONTROLLER_LISTENER_NAMES | KRaft | CONTROLLER | Sets controller.listener.names. |
| Variable | Mode | Required | Default / Behavior |
|---|---|---|---|
KAFKA_ZOOKEEPER_CONNECT | ZooKeeper | ✅ | If set, script removes KRaft keys from config. |
KAFKA_NODE_ID | KRaft | ✅ | Script exits if missing in KRaft mode. |
KAFKA_CLUSTER_ID | KRaft | ❌ | If absent and not formatted, script generates UUID. |
KAFKA_CONTROLLER_QUORUM_VOTERS | KRaft | ❌ | Defaults to ${KAFKA_NODE_ID}@localhost:9093 (single node). |
KAFKA_PROCESS_ROLES | KRaft | ❌ | broker,controller (single-node default). |
| Variable | Mode | Default | Description |
|---|---|---|---|
KAFKA_LOG_RETENTION_HOURS | both | none | Sets log.retention.hours. |
KAFKA_NUM_PARTITIONS | both | none | Sets num.partitions. |
KAFKA_AUTO_CREATE_TOPICS | both | none | Sets auto.create.topics.enable. |
| Variable | Mode | Default | Description |
|---|---|---|---|
KAFKA_HEAP_OPTS | both | none | e.g. -Xms512m -Xmx512m. |
KAFKA_JVM_PERFORMANCE_OPTS | both | none | Extra JVM flags (e.g. GC, server mode). |
KRaft (single node)
environment:
KAFKA_CONF: /etc/kafka
KAFKA_BIN: /opt/kafka/bin
KAFKA_DATA: /var/lib/kafka
KAFKA_NODE_ID: "1"
KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093"
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://your-host:9092"
KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka:9093"
KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT"
KAFKA_HEAP_OPTS: "-Xms512m -Xmx512m"
KAFKA_NUM_PARTITIONS: "3"
KAFKA_AUTO_CREATE_TOPICS: "true"
KAFKA_LOG_RETENTION_HOURS: "168"
---
## 🧩 Minimal `server.properties` templates
### KRaft (single node, combined controller+broker)
```properties
# roles
process.roles=broker,controller
node.id=1
# listeners
controller.listener.names=CONTROLLER
listeners=PLAINTEXT://:9092,CONTROLLER://:9093
listener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT
inter.broker.listener.name=PLAINTEXT
# discovery
advertised.listeners=PLAINTEXT://localhost:9092
controller.quorum.voters=1@localhost:9093
# logs & defaults
log.dirs=/var/lib/kafka/kafka-logs
num.partitions=1
auto.create.topics.enable=truec
Controller port 9093 generally does not need to be published externally; it’s used by the KRaft quorum.
services:
kafka:
image: mich43l/kafka
container_name: kafka
hostname: kafka
ports:
- "9092:9092" # client
- "9093:9093" # controller (optional to expose)
environment:
# Required
KAFKA_CONF: /etc/kafka
KAFKA_BIN: /opt/kafka/bin
KAFKA_DATA: /var/lib/kafka
# KRaft
KAFKA_NODE_ID: "1"
KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093"
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://localhost:9092"
KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka:9093"
# Optional tuning
KAFKA_HEAP_OPTS: "-Xms512m -Xmx512m"
KAFKA_NUM_PARTITIONS: "3"
KAFKA_AUTO_CREATE_TOPICS: "true"
KAFKA_LOG_RETENTION_HOURS: "168"
volumes:
- ./server.properties:/etc/kafka/server.properties:ro
- kafka-data:/var/lib/kafka
restart: unless-stopped
volumes:
kafka-data:
version: "3.8"
services:
zookeeper:
image: bitnami/zookeeper:3.9
environment:
ALLOW_ANONYMOUS_LOGIN: "yes"
ports: ["2181:2181"]
restart: unless-stopped
kafka:
image: mich43l/kafka
ports:
- "9092:9092"
environment:
KAFKA_CONF: /etc/kafka
KAFKA_BIN: /opt/kafka/bin
KAFKA_DATA: /var/lib/kafka
# ZooKeeper
KAFKA_ZOOKEEPER_CONNECT: "zookeeper:2181"
# Listeners
KAFKA_LISTENERS: "PLAINTEXT://0.0.0.0:9092"
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://localhost:9092"
volumes:
- ./server.properties:/etc/kafka/server.properties:ro
- kafka-data:/var/lib/kafka
restart: unless-stopped
volumes:
kafka-data:
docker compose up --build -d
docker compose logs -f kafka
docker exec -it kafka kafka-topics.sh \
--bootstrap-server localhost:9092 \
--create --topic demo --partitions 3 --replication-factor 1
docker exec -it kafka kafka-console-producer.sh --bootstrap-server localhost:9092 --topic demo
docker exec -it kafka kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic demo --from-beginning
The entrypoint traps SIGTERM and SIGINT, forwards them to the Kafka process, waits for exit, and returns the broker’s exit code.
You can add this to your Compose service:
healthcheck:
test: ["CMD", "bash", "-lc", "kafka-broker-api-versions.sh --bootstrap-server localhost:9092 >/dev/null 2>&1"]
interval: 15s
timeout: 5s
retries: 10
start_period: 20s
Mode auto-detection & default
KAFKA_ZOOKEEPER_CONNECT is set ➜ ZooKeeper mode.KAFKA_NODE_ID, KAFKA_CLUSTER_ID, or KAFKA_CONTROLLER_QUORUM_VOTERS is set ➜ KRaft mode.KAFKA_NODE_ID. The script exits if it’s missing.Patching semantics (idempotent-friendly)
# key=... line if present,key=... line,key=value to the end if absent.listeners, advertised.listenerszookeeper.connectnode.id, process.rolescontroller.quorum.voters, controller.listener.nameslistener.security.protocol.maplog.dirs, log.retention.hours, num.partitions, auto.create.topics.enableMode-specific sanitization
process.roles, node.id, controller.quorum.voters, controller.listener.names.zookeeper.connect.KRaft defaults (if you didn’t provide them)
listeners=PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093controller.listener.names=CONTROLLERlistener.security.protocol.map=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXTprocess.roles=broker,controller (single-node default; override via KAFKA_PROCESS_ROLES)Storage formatting (KRaft only)
${KAFKA_DATA}/kafka-logs/meta.properties exists, formatting is skipped (already formatted).KAFKA_CLUSTER_ID or generates one and runs kafka-storage.sh format.Listeners & reachability
advertised.listeners must be reachable by clients. Use a real host/IP for external producers/consumers.Baselines still required
server.properties from scratch.Paths & ownership
log.dirs is set to ${KAFKA_DATA}/kafka-logs; the directory is created if missing.chown -R mich43l:mich43l (non-fatal if user/group absent).mich43l or adjust the entrypoint and Dockerfile accordingly.Runtime preconditions
java must exist in $PATH; otherwise the entrypoint exits.KAFKA_CONF, KAFKA_BIN, KAFKA_DATA. server.properties must exist in KAFKA_CONF.Signals & lifecycle
set -euo pipefail for safer bash behavior.SIGTERM/SIGINT, forwards to Kafka, waits, and returns Kafka’s exit code.Common foot-guns
server.properties: Kafka takes the last one; your baseline + script appends may create multiple entries—prefer letting the script manage them.advertised.listeners must match how clients reach the broker (service DNS vs host IP).Content type
Image
Digest
sha256:f7250407e…
Size
403.7 MB
Last updated
8 months ago
docker pull mich43l/kafka:2.8.2