Running Apache Kafka in Docker with the official image

Running the official apache/kafka image in production means pinning a supported release, persisting log and metadata directories, and getting listener settings right for every client network. This page covers what to decide before you deploy.

On this page

Running Apache Kafka in Docker with the official image beyond a laptop

A single container that starts Kafka on a laptop proves very little about a deployment others can depend on. The first decision is which image and which version tag the cluster will run, and that choice starts from the supported releases on the Apache Kafka downloads page, which lists 4.3.1, 4.2.2 and 4.1.2 as supported at the time of writing. Pin an exact tag from that list rather than a floating tag, so that a restart or a reschedule never silently changes the broker version. Apache Kafka is an Apache Software Foundation project released under the Apache License 2.0, and the project itself offers no paid edition, so the official apache/kafka image is the community build and the cost of running it is the infrastructure, the people and any support contract you choose to add. The container does not change how Kafka behaves. Brokers still write partition logs and metadata to directories on disk, and they still advertise the addresses that clients use to reconnect. Anything a container runtime does to those directories, such as writing to an ephemeral layer, is a risk you own. Confirm the exact tags published for the image on the project's own release and repository pages before you write them into deployment manifests.

Persistence and addressing are the two areas where containers most often break Kafka quietly. Mount persistent volumes for both the log directories and the metadata directory used in KRaft mode, and verify after a container restart that the cluster ID and existing partition data are still present. Configuration is passed through environment variables in the image, so the listener, advertised.listeners and controller settings should be reviewed as a set: a broker that advertises a container-internal hostname will accept connections from other containers and then fail for clients outside the network. Decide early which listeners serve internal traffic, which serve external clients, and which resolvable names each one advertises. The image's native build is a separate artifact from the standard image, and it should not be used for production workloads; check the current guidance in the apache/kafka repository on GitHub before you choose an image variant.

Configuration options and when each fits

This table compares the settings a container deployment must decide before the first start. Read each row as a decision: when the setting fits, and what breaks if it is wrong. Exact environment variable names for the official image must be confirmed in the image documentation in the apache/kafka repository on GitHub before you write any compose file or manifest.

SettingWhen it fitsWhat breaks if it is wrong
process.roles and node.idUse broker,controller on a small single-node test or a combined three-node lab. Use separate broker and controller containers when controllers must stay stable while brokers scale or restart.Each container needs a unique node.id and roles that match its place in the quorum. Duplicate IDs or roles that do not match the quorum stop the node from joining or corrupt its view of the cluster.
listenersDefine one listener per network path: an internal listener for other containers, and a separate one for host or external clients. Bind to 0.0.0.0 inside the container so the port is reachable.If a listener is missing or bound to the wrong interface, clients connect but cannot complete the handshake, or the broker never becomes reachable from the network the client uses.
advertised.listenersSet each advertised address to the name or IP that clients on that network can resolve and reach, such as a Docker network alias for containers or the host address for outside clients.This is the most common container failure. Clients receive an address they cannot reach, so the bootstrap connection succeeds but produce and fetch requests time out. Changing it after data exists also requires a deliberate restart plan.
log.dirs on a persistent volumeMount a named volume or host path for the log directory on every broker and controller, and keep it separate from the container writable layer.If the directory is not persistent, a container recreation removes the log data and the metadata that identifies the cluster. The node then rejoins with an empty state, which can cause data loss or a formatting error on restart.
controller.quorum.bootstrap.serversUse it when controllers and brokers are started in separate containers or on separate hosts and need an explicit list of reachable controller endpoints to find the quorum.A wrong or unreachable endpoint list leaves brokers waiting for a quorum. Listing hostnames that resolve only inside one network breaks nodes on other networks.
listener.security.protocol.mapMap each listener name to PLAINTEXT, SSL or SASL variants only when TLS or SASL is planned. Keep controller and inter-broker listeners on a protocol the whole cluster supports.A listener left on PLAINTEXT when clients expect TLS exposes traffic on the network. A protocol mismatch between listeners causes failed connections that look like network faults.
Image tag pinningPin an exact released version tag for each environment and change it only through a tested upgrade. Use the supported release list on the Kafka downloads page to choose the tag.A floating or unversioned tag can pull a different release on restart, changing behavior or metadata format without a planned upgrade window.
Environment variable mappingConfirm each setting name and its mapping rule in the image documentation in the apache/kafka repository on GitHub for the exact tag you deploy.Names or formats that differ from the image documentation are silently ignored or misread, so the broker starts with defaults and the intended listener or storage layout never takes effect.

Setting up Apache Kafka in Docker for production

This sequence takes a single Kafka node or small cluster from a pinned image tag to a verified KRaft broker with persistent storage, cluster identity and client-facing listeners. Each step ends with a check so you know it is safe to move on.

  1. Pin a supported release tag

    Choose a release from the Kafka downloads page that is still supported, then pin the apache/kafka image to that exact version tag in your compose file or deployment manifest. Avoid floating tags such as latest, so a container restart never pulls a different version. Record the tag in your change log and in the configuration repository. Confirm the tag exists on the image registry and matches the release you selected. Also confirm that the image you choose is the standard JVM image and not the native image, since the native image is not intended for production use. Check the image documentation for the exact tag naming scheme for your version.

  2. Create persistent volumes for log and metadata

    Create separate persistent volumes or host directories for the log directory, which holds topic partition data, and the metadata directory, which holds the KRaft metadata log. Mount both into the container at the paths your configuration will reference, and keep them on storage that survives container replacement. Ownership must allow the container user to write to both paths. Check by starting a throwaway container with the same mounts and confirming that files can be created and survive a container restart. Do not share a volume between two broker containers.

  3. Generate a cluster ID

    Generate a cluster ID with the storage tool included in the image you pinned, and store the value in your configuration source so every node uses the same ID. Run the generation command once per cluster, not once per node. Confirm the command returns a single non-empty identifier and record it with the cluster's other deployment values. The exact command and its arguments depend on the image version, so confirm them against the image documentation before running it.

  4. Set node ID and process roles

    Assign a unique node ID to each container and set process roles so each node runs as a broker, a controller, or both, according to your planned layout. Give every node a distinct ID in its environment configuration, and make sure the controller quorum voters list matches the controller nodes you intend to run. Mixing roles on a single node is acceptable for small test environments but changes failure behavior, so document the choice. Check by reading the rendered configuration inside the container and confirming the node ID and roles match your plan on every node.

  5. Configure listeners and advertised listeners

    Define one listener per client network, such as an internal listener for other containers and an external listener for clients on a host network, each with its own port and protocol. Set the advertised listeners to the address each client network actually resolves to, because clients connect to whatever address the broker advertises in its metadata. Advertising a container name or an internal IP to external clients is the most common cause of connection failures after startup. Confirm each advertised address resolves and is reachable from the network it is meant for. Confirm the security protocol map and any inter-broker listener match your security plan.

  6. Start the container and confirm the quorum

    Start the container with the mounted volumes, cluster ID, node settings and listeners in place, then read the container logs until the broker reports it has started and joined the quorum. Check that no errors about storage formatting, cluster ID mismatch or listener binding appear. If a volume already holds data from a different cluster ID, stop and resolve the mismatch rather than reformatting over it. Confirm the container stays running after its first minute, since a crash loop usually points to configuration or storage problems.

  7. Verify metadata with a describe check

    Run a metadata check with the Kafka command-line tools included in the image, such as describing the cluster or the quorum state, to confirm the broker is registered and the controller quorum has a leader. Compare the reported broker ID, advertised endpoints and cluster ID against your plan. A mismatch in advertised endpoints means clients will fail even if the broker is healthy. Confirm the quorum reports the expected number of voters before continuing, and confirm the command names exist in your pinned version.

  8. Run a produce and consume test

    Create a test topic with an explicit partition count and replication factor that matches your node count, then produce a few messages from a client on the external listener and consume them from a separate consumer group. Confirm every message is read back in order within a partition and that the consumer group offset advances. Delete the test topic afterward. This test proves the advertised listeners, the storage and the metadata path work end to end, but it does not prove security controls, so repeat it after enabling authentication.

Running Apache Kafka in Docker: listener settings by environment

Clients running in other containers on the same Docker network should receive the broker's container service name, which that network resolves. Kafka returns advertised addresses in metadata, so a client that bootstraps successfully can still fail when it connects to the address the broker advertised.

  • Advertised address: the service name of the broker container and its internal port, for example kafka-1:9092 (illustrative).
  • Separate internal listener: bind an internal listener to the container network so it stays distinct from any host-facing listener.
  • Localhost trap: an advertised value of localhost sends other containers back to their own network namespace, where nothing listens.
  • Name resolution: confirm that client containers resolve the broker names through the same Compose or Docker network.
  • Multiple brokers: each broker needs its own advertised name, and every broker must be reachable from every other container.

Confirm from a client container on the same network that the metadata response returns the service names and that a produce and consume round trip completes.

Why the native image is not for production

The project publishes more than one image build for Kafka, and the native build should not be your production choice. A native image compiles the broker ahead of time into a platform binary instead of running it on a Java virtual machine. That can shorten startup and simplify packaging, but it changes the runtime under the broker. The JVM image is the runtime the project tests and documents for running brokers and controllers, including the garbage collector and Java settings covered in the JVM guidance. Choosing the native build means you are running a configuration that your broker tuning and troubleshooting references may not describe.

Treat the status of each image variant as something you confirm at the time of deployment, not something you carry over from an earlier project note. Read the current image documentation in the official apache/kafka repository and the release notes for the version you plan to pin. Check whether each variant is labeled as production-ready, experimental or limited, and whether the release you pick publishes that variant at all. Record the answer in your change record so that an upgrade to a newer tag does not silently switch you to a different build.

Validate the JVM image in staging with the same version tag, environment variables, volumes and listener settings you plan to run in production. Start a controller and a broker, restart them, fill and delete test topics, and confirm that data and metadata survive a container recreation. Put the same tests on the native variant only if the project documents it as supported for your use case, and keep the JVM image as the default unless that documentation says otherwise. Ask your platform team to own the image choice as a documented decision, since the image sets the runtime your security patching and Java updates depend on.

Pinning version tags to supported Kafka releases

Image tags decide which Kafka code runs in your cluster, so the version choice is a platform decision, not a container detail. The cards below cover the tag, the supported release list, the upgrade window, the Java runtime and the test cycle that every rebuild should pass through.

Pin an exact version tag

Reference a full version tag such as apache/kafka:4.3.1 in every Compose file, Helm value and deployment manifest, and never use latest. A floating tag can change the broker version on a restart or a pull, which turns a routine redeploy into an unplanned upgrade. Check that every environment, including staging, names the same tag so test results apply to production.

Run only supported releases

Limit new deployments to the releases the downloads page lists as supported: 4.3.1, 4.2.2 and 4.1.2. Earlier versions are archived and stop receiving bugfix releases, so a cluster on an archived line carries known defects with no upstream fix. Confirm the list on the downloads page before you approve a version for a new cluster.

Plan upgrades inside the rolling window

The time-based release plan supports rolling upgrades from each release in the past year, which is the last three releases, to the latest version. If your cluster falls outside that window, plan a stepped path through an intermediate release rather than one jump. Schedule each move against the release cadence so that you are not chasing a version that is already leaving support.

Confirm Java 17 for brokers

Since Kafka 4.0, brokers, Kafka Connect and tools require Java 17, while clients and Kafka Streams applications need at least Java 11. Check that the base image you rebuild from provides a Java 17 runtime, and that the Java patch level meets your security policy. A custom image built on an older base can start with the wrong runtime and fail only after deployment.

Test every image rebuild

Treat each rebuilt image as a change, even when the Kafka version stays the same. A new base layer, a plugin, a Connect connector or a Java patch can change behavior, so run startup, produce and consume, and restart tests in a non-production cluster before promotion. Keep the previous tag available so that rollback is a tag change, not a rebuild.

Shape the patch process around cadence

Kafka plans three releases a year, one every four months, and issues bugfix releases only for supported versions. Set a standing review each quarter to compare your running tag with the downloads page, and decide in advance who approves a patch move. This keeps patch work predictable instead of reacting to each announcement.

Securing the containerized listeners and credentials

Kafka security is optional, so a container deployment has to make every security decision explicitly. The official image will start a broker with plaintext listeners if you let it, and a broker that accepts unauthenticated connections on a published port is the most common exposure in container setups. Start by listing every entry in KAFKA_LISTENERS and its protocol, then remove any PLAINTEXT listener that clients outside a trusted network segment can reach, or confine it to an internal listener that only the brokers and controllers use. Map each listener name to a protocol in KAFKA_LISTENER_SECURITY_PROTOCOL_MAP so that the intent is visible in the environment file rather than inferred from defaults. Confirm the exact variable names and their behavior for the image tag you pin, because the image documentation and the Kafka configuration reference are the authority for them.

Encryption and authentication are separate choices. SSL/TLS encrypts traffic between clients, brokers and tools, and Kafka also supports SASL for authentication. The SASL mechanisms documented by the project are GSSAPI (Kerberos), PLAIN, SCRAM-SHA-256, SCRAM-SHA-512 and OAUTHBEARER. For most container deployments, SCRAM-SHA-512 over TLS is the simplest choice to operate without an external directory, but the decision depends on your identity estate, so record it in the design before writing the compose file or manifest. Authorization is a third layer. In KRaft clusters, the StandardAuthorizer stores ACLs in the cluster metadata log, and any resource without an ACL is restricted to super users unless allow.everyone.if.no.acl.found is set to true. Leave that setting at its default, define super users deliberately, and test that a non-admin client is denied before you publish any topic.

Credentials must reach the container at runtime, never inside the image layers. Keystores, truststores, SCRAM passwords and the controller and broker credentials belong in a secret store or in files mounted read-only into the container, and the broker should read them through file-based configuration where the image supports it. Do not place secrets in docker run arguments or in committed environment files, because they appear in process listings, shell history and image inspection output. The exact mounting method depends on your runtime (plain Docker, Docker Compose, Kubernetes, or a secrets manager that injects values), and you should confirm which mechanisms your chosen image tag and runtime support before you rely on one. Plan for rotation as well: a keystore or SCRAM credential change should not require rebuilding the image, and you should test that a restarted broker picks up the new material cleanly.

Operational risks of running Kafka in containers

Containers make Kafka easy to start and easy to lose. The risks below share a root cause: broker state, identity and configuration live in places the container runtime can replace. Each card names the early symptom and the control that keeps the failure from reaching production.

Data loss on container recreation

Kafka keeps partition data and KRaft metadata in the directories set by log.dirs and metadata.log.dir. If a container is removed or rescheduled without a persistent volume mounted on those paths, the broker starts with empty storage and its replicas must be rebuilt from other brokers. Check that every broker and controller mounts a named or host volume on both directories before the first start. Early symptom: partition counts stay the same but log end offsets reset, and replicas appear out of sync after a restart.

Broker identity drift

A KRaft node keeps its node ID and cluster ID in the metadata directory it was formatted with. If a container starts with a new node ID, a different cluster ID, or a volume formatted for another cluster, the node either refuses to join or joins under a mismatched identity. Fix node IDs in the deployment manifest, store the cluster ID as a managed value, and never format a volume that already holds data. Early symptom: startup errors about inconsistent cluster IDs or a controller quorum that does not include the expected voters.

Unreachable advertised listeners

Clients connect to the bootstrap address, then reconnect to the address each broker advertises. Inside a container network, the advertised value is often the container hostname, which external clients cannot resolve. Define separate listeners for internal, controller and external traffic, and set advertised.listeners to the address each client group can reach. Early symptom: the initial connection succeeds, metadata requests return, and then producers and consumers time out on produce or fetch requests.

Upgrades that skip the supported path

Changing an image tag is the easiest step in the whole process, and the one most likely to bypass the documented route. Rolling upgrades are supported from the last three releases to the latest version, and clusters on older KRaft versions are advised to reach 3.9.x before moving to 4.x. Pin exact tags, record the release you are on, and plan each step against the upgrade notes. Early symptom: brokers fail to start after an image change, or a version mismatch appears between controllers and brokers.

Unmanaged JVM and host settings

The container inherits the host kernel, so limits such as open file descriptors and vm.max_map_count come from the host, not the image. The JVM also sizes its heap from container limits unless heap options are set explicitly. Set memory limits with enough headroom for page cache, pin the Java version through the image, and verify limits from inside the running container. Early symptom: Too many open files errors under load, or broker restarts from memory pressure that do not match the configured heap.

Missing metrics and log collection

A containerized broker writes logs to standard output and exposes JMX metrics on a port that is not exposed by default. If no collector scrapes these streams, under-replicated partitions, controller election events and disk alerts go unseen until clients report errors. Decide which JMX exporter or agent will run, which log driver or shipper will collect output, and which alerts must exist before go-live. Early symptom: dashboards show gaps around restarts and no alert fired before the incident.

Questions buyers ask before running Kafka in Docker

What does a production Kafka container setup cost in effort, and what drives it?

The cost is mostly people time, and it scales with the number of broker and controller nodes, the networks they sit on, the security scope and the depth of testing. A single-node lab is cheap to build, but a multi-node KRaft cluster with separate listeners, TLS, authentication, authorization, persistent volumes and backups needs far more design and verification work. Infrastructure costs are separate and depend on your hosts or cloud accounts. Ask any quote to break the work into design, build, security configuration, testing and handover, and to state which items are one-time and which recur, such as patching, upgrades and monitoring. Kafka itself is licensed under Apache-2.0 with no paid edition from the project, so license cost is not a line item.

Can our team run Kafka in Docker without outside help?

Yes, if the team already operates Kafka and containers well. The skills that matter are Kafka operations (KRaft controllers, brokers, partitions, retention), container networking (how advertised listeners resolve from inside and outside the container network), and TLS and authentication (certificate issuance, SCRAM or other SASL mechanisms, and ACLs). Most teams find the listener and advertised listener settings the hardest part, because a mismatch often looks like a working cluster until a client connects from another network. An outside partner is useful when none of these skills exist in-house, or when you need a second set of eyes on the security configuration before production.

How long does a Docker-based Kafka deployment take, and what changes the duration?

Duration depends on how many decisions are already made. Teams that arrive with an approved network design, a certificate authority, storage and a backup policy usually move faster than teams that still need to define them. The main drivers are the number of nodes, whether TLS and SASL are required from day one, how many client applications must be tested against the new listeners, and whether a staging cluster exists. DeployOpen does not publish timelines. A written scope will state the phases and their order, and the quote should show what the client must provide before each phase can start.

What do we need to provide before the work starts?

Plan to supply a network diagram that shows which subnets, hosts and load balancers can reach the brokers and controllers, plus the client networks that will connect. You also need an internal certificate authority or a decision on how certificates will be issued and rotated, a volume and backup policy that defines where log and metadata directories live and how they are restored, and a staging cluster where configuration can be tested before production. Access to the container host or orchestrator, a container registry that your hosts can pull from, and named owners for security and operations decisions speed up the work and reduce rework.

Can a Docker deployment move to Kubernetes later?

It can, but the move is a migration rather than a configuration change. The data directories, KRaft metadata and listener design carry over only if they are planned for it, and advertised listeners that point at container hostnames or fixed host ports will need to be redesigned for pod networking. Kubernetes deployments are typically managed with an operator, so the configuration model and the storage classes differ from plain containers. Before building the Docker layout, document the volume paths, the listener names and the certificate hostnames so they can be mapped later. Confirm the target platform and operator choice with your team before the move is scoped.

Is the native Kafka image suitable for production?

The native image is not recommended for production use, so production deployments should use the standard apache/kafka image with a pinned version tag. Pinning matters because a floating tag can change the broker version during a restart. Check the image and release notes in the project repository and the Kafka downloads page before you choose a version, and confirm which releases are currently supported. A quote should state which image variant and tag will be used and how image updates are tested before they reach production.

What ongoing work follows the initial deployment?

After go-live, the cluster needs regular work: applying patch releases, planning minor upgrades within the supported releases, rotating certificates, reviewing ACLs, checking disk and retention, and restoring from backups on a test schedule. Kafka publishes bugfix releases for supported versions only, so the image tag must move forward on a schedule. Ask any quote whether these tasks are included in the build, handed to your team as a runbook, or offered as an ongoing support retainer.

How DeployOpen scopes and delivers a containerized Kafka deployment

DeployOpen plans a containerized Kafka deployment from written requirements: image version, storage layout, listener design and access model. The work is delivered inside your own infrastructure, with the container configuration, verification and runbooks handed over to your team.

How we scope

  • Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts.
  • Scope records the pinned apache/kafka image tag, the target Kafka release line and the upgrade policy you will follow.
  • Scope lists listener and advertised listener design for internal, external and cross-network clients, with your network team confirming routes.

How we build

  • We write container definitions with configuration through environment variables, persistent volumes for log and metadata directories, and KRaft settings.
  • We test broker restarts, volume reattachment and client connections from each network path you need to support.
  • We confirm the production build uses the JVM-based image rather than the native image, and document the reason in the design record.

How we hand over

  • You receive the container configuration, volume layout, listener map and version pinning policy as version-controlled files.
  • Runbooks cover starting, stopping, rolling restarts, image updates and recovery of a failed node from its persistent volume.
  • We walk your operators through verification steps so they can confirm the cluster after every change without our help.

How we support

  • Ongoing support, upgrades and continuous testing are available as retainers, scoped separately from the build.
  • Support can cover moving to a newer Kafka release from a supported line, with the upgrade path confirmed against the upgrade notes.
  • Retainer scope should state response expectations, image rebuild cadence and who approves changes to listeners and access rules.

Sources and further reading

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.