Skip to main content

Overview

Apache Kafka is a distributed event streaming platform for high-throughput, fault-tolerant publish-subscribe messaging. This template deploys a Kafka cluster in KRaft mode (no ZooKeeper): every broker is also a KRaft controller, the client listener authenticates with SASL/PLAIN, Kafka ACLs are enforced, and each broker keeps its logs on persistent volumes with daily snapshots. A health sidecar and two Prometheus exporters run beside every broker. Kafbat UI (a web console), a Confluent REST Proxy and a Kafka CLI client workload are on by default; Kafka Connect workers and a public listener for clients outside the GVC are opt-in.
This template deploys into an existing GVC — the one you pass with --gvc. It does not create, provision or manage a GVC, and uninstalling the release never deletes one.

What Gets Created

Prerequisites

1

Create the Kafbat UI configuration secret

Kafbat UI is on by default and reads its whole configuration from an opaque secret mounted as a file. The secret must exist before you install. Replace ADMIN_PASSWORD with the value you will pass as kafka.listeners.client.sasl.admin.password, and UI_PASSWORD with the login you want for the console. The full file format is in the Kafbat configuration reference.
kafka-kafbat-ui-config is the default value of kafbat_ui.configuration_secret; if you pick another name, set that value to match. If you do not want the console, install with --set kafbat_ui.enabled=false instead and skip this step.
If the secret is missing at install time the install still succeeds, cpln logs returns nothing for RELEASE_NAME-kafbat-ui, and the workload never becomes ready. The only place the missing secret is named is status.versions[].message:
Create the secret and the deployment recovers on its own after a few minutes, or force it with cpln workload force-redeployment RELEASE_NAME-kafbat-ui --gvc GVC_NAME.
2

Choose the cluster credentials

The KRaft cluster ID and the SASL passwords are template values, set at install time. Generate a cluster ID with the Kafka tooling (the same image the template runs):
Pick strong values for kafka.secrets.inter_broker_password, kafka.secrets.controller_password, kafka.listeners.client.sasl.admin.password and kafka.listeners.client.sasl.passwords. The your-… defaults in values.yaml are placeholders that work as literal passwords, so never leave them in place. The cluster ID is written into the log volumes at first start and cannot be changed afterwards without a new cluster.
3

Public listener (optional)

Exposing brokers to clients outside the GVC needs a domain you control and a dedicated load balancer enabled on the GVC. Follow the Configure Domain guide first, then see Public Listener below.

Installation

Install into an existing GVC, supplying the credentials from the prerequisites:
The REST Proxy ships pointing at a bootstrap address and admin password that only match a release named kafka-dev with the default password. Either put kafka_rest_proxy.properties.bootstrap.servers, kafka_rest_proxy.jaas_conf and kafka_rest_proxy.password_properties in a values file (-f values.yaml) as shown under Kafka REST Proxy, or add --set kafka_rest_proxy.enabled=false.

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

Each block below shows the shipped defaults for one area of values.yaml. Only the keys you change need to go in your values file.

Kafka Cluster

Storage

With customEncryption enabled, finish the setup after installing: open each RELEASE_NAME-logs-N volume set in the console, go to its spec and follow the AWS Custom Encryption Instructions shown there. Existing volumes cannot be re-encrypted.

Firewall

Listeners

Every entry under kafka.listeners becomes a broker listener. The chart ships one, client; a second entry is how you add external access. Ports 9091, 9093 and 9094 are reserved for the controller and inter-broker listeners.

Client Listener

Public Listener

Add a second listener to reach the brokers from outside the GVC. TLS is terminated at the load balancer, so external clients connect with security.protocol=SASL_SSL. Only one listener may carry a public address. Direct replica routing (recommended). One domain with a DNS01 certificate challenge; each broker is advertised at <replica>-<location>.<publicAddress>:<containerPort> (for release kafka in aws-us-east-1: kafka-cluster-0-aws-us-east-1.kafka.example.com:9095, kafka-cluster-1-aws-us-east-1.kafka.example.com:9095, …). Create a CNAME for each broker hostname plus the _acme-challenge record as the domain reference describes.
Multi-port routing (legacy). One port per broker starting at 3000 on a single hostname (kafka.example.com:3000, :3001, :3002). Do not set containerPort here. Not recommended for multi-zone clusters, because traffic is not routed zone-aware.

ACL and Cluster Secrets

Extra Configurations

Keys under kafka.extra_configurations are appended to server.properties and override the chart’s defaults. The chart derives default.replication.factor as the smaller of 3 and kafka.replicas, and min.insync.replicas as one less than that; set them here only to override.

Health Sidecar and Exporters

All three run as sidecar containers on RELEASE_NAME-cluster. The health sidecar gates broker readiness on real cluster health (broker registration, controller election, under-replicated partitions, log directories); the exporters publish Prometheus metrics that the platform scrapes automatically.

Kafbat UI

Kafka REST Proxy

The proxy connects to the brokers as the KafkaClient principal in jaas_conf, and authenticates HTTP callers with BASIC auth against the users in password_properties. Both must be set for your release: bootstrap.servers must name your cluster workload and the KafkaClient password must equal kafka.listeners.client.sasl.admin.password. Without password_properties the BASIC realm has no users and every request is refused.

Kafka Connect

kafka_connectors is a list; each entry deploys a Kafka Connect worker cluster named RELEASE_NAME-connect- plus the entry’s name, with a volume set for plugins and a plugins-downloader container that fetches plugin artifacts into plugins_folder (see Kafka Connect Plugins). Connectors listed under plugins are created or updated through the Connect REST API when the worker starts; an entry with enabled: false is removed. The shipped values.yaml carries complete, commented examples for MirrorMaker 2, a Camel S3 sink, a ClickHouse sink and a Snowflake sink.

Kafka Connect Plugins

Every Kafka Connect replica runs two containers: kafka-connect and plugins-downloader. The downloader fetches every artifact of each plugin that has enabled: true onto the plugins volume set. Kafka Connect scans its plugins folder only once per start, so it waits until every artifact is in place before it starts. Each artifact is downloaded to a staging area on the volume, verified, and only then moved into its own directory (<plugins_folder>/<plugin>/<key>/), so in a normal start Kafka Connect never sees a half-written file. An artifact that is already installed and unchanged is not downloaded again, so a restart does not repeat the downloads.
  • enabled: true is required for a download. A plugin without the key is not downloaded, but its connector is still created; use that for a plugin you placed in the plugins folder yourself. With enabled: false the connector is deleted, and the artifacts the chart downloaded for it are removed.
  • Set sha256 on every artifact. The downloader does not verify TLS certificates, so the checksum is the integrity check. On a mismatch the artifact is not installed and the download is retried. Without sha256, a jar or archive is still checked to be a valid file before it is installed.
  • Each artifact gets its own directory, so a plugin with several jar artifacts keeps all of them, and they load in one classloader.
  • Files you placed on the volume yourself are never deleted. The chart removes only artifacts it downloaded, only after a download pass succeeds, and nothing while any artifact has a configuration error (an unsupported type, a missing URL). The downloader lists the files it leaves alone at each start (left untouched (not downloaded by this chart)).
  • Changing an artifact’s url or sha256 downloads that artifact again; the old copy is removed once the pass succeeds. Size the volume for every installed artifact plus room to download and unpack the largest one.
  • plugins_wait_timeout_seconds and plugins_wait_timeout_action decide what happens when a download is still missing at the timeout: start starts Kafka Connect without it and logs a WARNING naming it; restart exits so the container restarts and waits again. See Troubleshooting.
  • plugins_redownload_token forces a fresh download of every plugin: set it to a new value and upgrade the release.
  • downloader_cpu defaults to 80m. Raise it to 250m when an artifact is 50 MB or larger, so the download finishes well inside the wait timeout.
  • Credentials in an artifact URL (a user:password@ prefix on the host name) are sent only to that URL’s host; a redirect to another host, such as a presigned storage URL, is followed without them. They are redacted in the logs, but they are stored in the RELEASE_NAME-connect-<name>-download secret and in the Helm release. Percent-encode /, ?, # and @ inside the user name or password.
  • The Kafka Connect workload reports ready while it waits for plugins. Read the kafka-connect log for Waiting for plugin downloads to see that it is still waiting.

Kafka Client

Connecting

Read a canonical endpoint from the workload:
Verify the cluster from the bundled client workload. Open a shell on it, write a client properties file with the admin credentials, then list topics, produce and consume:
With a PLAINTEXT client listener, drop the --command-config, --producer.config and --consumer.config flags. Topics are created on first use because auto.create.topics.enable is true. To check the REST Proxy from a workload in the same GVC, list the clusters it sees (the v3 API) with one of the BASIC-auth users you configured:

Operations

Backing Up

Kafka’s first line of defence is replication: with the default three brokers, every topic is replicated three times and a write is acknowledged only once two replicas have it. On top of that, each broker volume set takes a snapshot daily at 00:00 UTC, keeps it for seven days and takes a final snapshot when the volume set is deleted (kafka.volumes.logs.snapshots). Take an extra snapshot before risky changes, once per log-directory volume set:

Restoring a Backup

Restoring a volume snapshot onto a running broker has not been verified for this template; a broker whose log directories are replaced underneath it, or whose KRaft metadata no longer matches its peers, is not something this page can promise will rejoin cleanly. Treat snapshots as a last resort and rely on replication for broker loss. What the chart does support is mounting volume sets that were restored or renamed outside the chart: list them, in kafka.logDirs order, and the chart stops creating its own and mounts yours instead, leaving their data and settings untouched. Use "" for any entry the chart should keep managing.
The list must have exactly one entry per kafka.logDirs path, or rendering fails with an error naming both counts.

Upgrading from 2.x

Since 3.0.0 the chart only supports Apache Kafka images. A values file that still sets a Bitnami image fails to render with Error: This chart does not support Bitnami images, please use Apache Kafka images instead. Set kafka.image to an apache/kafka tag. Moving the data of a 2.x Bitnami-based release onto 3.x or later has not been verified; plan for a new release and re-mirror topics (the kafka_connectors MirrorMaker 2 example in values.yaml is one way).

Upgrading from 3.x

  • 4.0.0 added the kafka-orchestrator readiness sidecar (set kafka_orchestrator: null to opt out), raised the graceful shutdown window to kafka.terminationGracePeriodSeconds: 600, and started deriving default.replication.factor and min.insync.replicas from kafka.replicas. A default.replication.factor you carried over in kafka.extra_configurations still wins; remove it unless you need a non-default value.
  • 4.0.2 commented out the sample REST Proxy user. If you relied on the default user BASIC-auth account, set kafka_rest_proxy.password_properties explicitly.
  • 4.1.0 added kafka.rackAwareness, kafka.volumes.logs.externalVolumeSets and per-connector tags; all off by default, nothing to change.
  • 4.2.0 stops the brokers writing a duplicate server.log to the container filesystem on apache/kafka:3.x images by setting KAFKA_LOG4J_ROOT_LOGLEVEL=INFO; the broker log is in cpln logs. The upgrade restarts the brokers.

Upgrading from 4.2.x

4.3.0 changes only how Kafka Connect gets its plugins: 4.2.x slept a fixed 60 seconds before starting, so a slow download could leave a connector missing or FAILED; 4.3.0 waits for the downloads to finish (see Kafka Connect Plugins). Nothing in your values has to change.
  • The upgrade restarts every Kafka Connect replica, one at a time, and the brokers.
  • The first start after the upgrade downloads every plugin again, once per replica, into the new layout, and Kafka Connect waits for it. Expect each replica’s connectors to be down for about one full download. Later restarts reuse the installed plugins.
  • The chart’s old <plugin>/<plugin>.jar files, for plugins that still have a jar artifact, are moved to .cpln-downloads/retired/ in the plugins folder. A file you placed at exactly that path is moved too, and can be moved back.
  • Directories that earlier versions extracted from zip, tar or tgz artifacts stay in place, and Kafka Connect still loads them beside the new copy. Once your connectors are RUNNING, remove them on each replica (RELEASE_NAME-connect-<name>-0, -1, …), then redeploy. The candidates are the entries the downloader lists at start as left untouched (not downloaded by this chart), minus any you placed yourself; directories named after a plugin’s name, .cpln-downloads and lost+found are never candidates. The commands below are for a connector entry named cluster; in a GVC with more than one location, add --location LOCATION to each exec:
  • 4.2.x printed artifact URLs, including any credentials in them, in the downloader log; 4.3.0 redacts them. Consider rotating any credential that was part of an artifact URL.
  • Rolling back to 4.2.x works and the connectors keep running, but 4.2.x writes its <plugin>/<plugin>.jar files again beside the 4.3.0 directories (both load in one classloader) and its 60-second startup race returns. Upgrading to 4.3.0 again retires those files.

Scaling and Availability

  • kafka.replicas accepts 1, 3, 4 or 5. Rendering fails with Invalid value for kafka.replicas. It must be less than or equal to 5 and not equal to 2. otherwise. Every broker is also a KRaft controller, so never shrink a running cluster below the number of controllers it was formed with — the quorum is lost and the cluster stops.
  • A single-broker cluster replicates nothing; use it for development only.
  • kafka.multiZone: true spreads the brokers across availability zones. On AWS, add kafka.rackAwareness.enabled: true and set client.rack on consumers to their zone ID (workloads on AWS locations receive it in the AWS_ZONE_ID environment variable) so they fetch from a same-zone replica; producers and replication are unaffected.
  • Broker volumes grow automatically up to kafka.volumes.logs.autoscaling.maxCapacity when free space drops below minFreePercentage.
  • Kafbat UI, the REST Proxy and each Kafka Connect worker scale with their own replicas value.

Troubleshooting

Broker output is in cpln logs; filter server-side:
Cause: the configuration secret named in kafbat_ui.configuration_secret did not exist when the workload was created. The container never starts, so there is no log output.Fix: read status.versions[].message from cpln workload get-deployments RELEASE_NAME-kafbat-ui --gvc GVC_NAME -o yaml to see which secret is missing, create it as shown in Prerequisites, then wait a few minutes or run cpln workload force-redeployment RELEASE_NAME-kafbat-ui --gvc GVC_NAME.
Cause: kafka.replicas is 2 or more than 5. Two controllers cannot form a quorum, and the chart caps the cluster at five.Fix: set kafka.replicas to 1, 3, 4 or 5.
Cause: kafka.image, kafka_client.image or a connector image references a Bitnami image, which the chart stopped supporting in 3.0.0.Fix: use an apache/kafka tag such as apache/kafka:3.9.1.
Cause: a listener’s containerPort (or directReplicaRouting.containerPort) uses one of the ports reserved for the controller and inter-broker listeners.Fix: pick another port, for example 9092 for the client listener and 9095 for a public one.
Cause: externalVolumeSets is set but its length does not match the number of comma-separated paths in kafka.logDirs. The chart refuses a partial list so it never mounts an empty new volume set beside imported ones.Fix: provide exactly one entry per log directory, in the same order, using "" for any directory the chart should keep managing.
Cause: kafka_rest_proxy.properties.bootstrap.servers still has the shipped default SASL_PLAINTEXT://kafka-dev-cluster:9092, which only resolves for a release named kafka-dev, or the KafkaClient password in kafka_rest_proxy.jaas_conf does not match kafka.listeners.client.sasl.admin.password.Fix: set bootstrap.servers to SASL_PLAINTEXT://RELEASE_NAME-cluster.GVC_NAME.cpln.local:9092, align the KafkaClient password, add at least one user under kafka_rest_proxy.password_properties, and upgrade the release.
Cause: the configuration secret is read when the container starts; updating the secret does not restart the workload.Fix: run cpln workload force-redeployment RELEASE_NAME-kafbat-ui --gvc GVC_NAME after changing the secret.
Cause: an artifact was still missing when plugins_wait_timeout_seconds ran out: a large or slow download, a dead URL or a sha256 mismatch. With the default plugins_wait_timeout_action: start, the kafka-connect log shows WARNING: connector plugins not ready after 900s; starting Kafka Connect WITHOUT: followed by the missing artifacts, and connectors that need them fail. With restart it shows FATAL: connector plugins not ready after 900s; exiting so the container restarts and waits again. and the container restarts. Read the log (for a connector entry named cluster):
Fix: fix any URL or checksum named in an ERROR download failed or ERROR sha256 mismatch line. For large artifacts, raise downloader_cpu to 250m or raise plugins_wait_timeout_seconds; use plugins_wait_timeout_action: restart if Kafka Connect must never run without a plugin. Once the download succeeds, the downloader logs all artifacts are now installed; Kafka Connect started without some of them, so restart the worker (cpln workload force-redeployment) to load them. Then run cpln workload force-redeployment RELEASE_NAME-connect-cluster --gvc GVC_NAME. Restarting only the kafka-connect container does not wait again and does not clean up; a redeployment does both.
Cause: its plugin arrived after Kafka Connect scanned the plugins folder. Restarting the connector through the REST API does not rescan the folder.Fix: run cpln workload force-redeployment RELEASE_NAME-connect-cluster --gvc GVC_NAME.
Cause: installed artifacts are reused on every restart, so a plugin whose file changed on the server keeps the copy already on the volume.Fix: set plugins_redownload_token on the connector entry to a new value (any string) and upgrade the release. Every plugin of that entry is downloaded again and the old copies are removed once the download pass succeeds.

Important Notes

  • Replace every your-… placeholder (kafka.secrets.*, kafka.listeners.client.sasl.*) before installing; they are accepted as literal passwords. They are plain template values and therefore land in the Helm release.
  • kafka.secrets.kraft_cluster_id is fixed once the brokers have started; changing it later means a new cluster.
  • kafka.replicas must be 1, 3, 4 or 5. Never shrink a running cluster below its controller count.
  • Kafbat UI and the REST Proxy are open to the internet by default (external_inboundAllowCIDR: 0.0.0.0/0); set a strong console login and BASIC-auth users, or narrow the CIDR.
  • The REST Proxy defaults only work for a release named kafka-dev: set kafka_rest_proxy.properties.bootstrap.servers, kafka_rest_proxy.jaas_conf and kafka_rest_proxy.password_properties for your release, or disable it.
  • ghcr.io/kafbat/kafka-ui, confluentinc/cp-kafka-rest:latest and the JMX exporter image are not pinned, so a redeploy may pick up a newer build; pin a tag in your values if that matters.
  • Uninstalling deletes the volume sets and every topic’s data; a final snapshot is kept for retentionDuration because createFinalSnapshot is true.
  • Only set KAFKA_LOG4J_ROOT_LOGLEVEL in kafka.env on apache/kafka:3.x images; on a 4.x image it removes every output from the root logger, including cpln logs.
  • kafka_ui (Provectus Kafka UI) is deprecated and off by default; use kafbat_ui.
  • Changing broker values restarts the brokers; schedule upgrades accordingly.
  • Set sha256 on every Kafka Connect plugin artifact: the plugin downloader does not verify TLS certificates.

External References

Apache Kafka Documentation

Official Apache Kafka documentation

KRaft Mode

Kafka KRaft consensus mode without ZooKeeper

Kafbat UI Configuration

Configuration file reference for the Kafbat UI secret

Confluent REST Proxy API

REST Proxy v2 and v3 API reference

Kafka Template

View the source files, default values, and chart definition