Skip to content

Kafka

Apache Kafka distributed event streaming platform. This chart uses the official apache/kafka image with a KRaft-only design — no ZooKeeper required. It supports a single-broker topology for development and a cluster topology for production-oriented workloads with dedicated or combined controller and broker roles.

KRaft only — ZooKeeper is not used or supported

This chart runs Kafka in KRaft mode (Kafka 4.x+). There is no ZooKeeper deployment, dependency, or compatibility mode. Migrations from ZooKeeper-based deployments require a separate KRaft migration process outside the scope of this chart.

Key Features

  • KRaft-only — no ZooKeeper, using Apache Kafka’s native consensus protocol
  • Single-broker mode — one-pod topology for development and CI environments
  • Cluster mode — dedicated controller and broker StatefulSets for production
  • Combined modebrokers.replicaCount: 0 makes controllers also act as brokers (3-node HA without 6 pods)
  • Stable broker DNS — advertised listeners use StatefulSet pod DNS names
  • JMX metrics — optional Prometheus-compatible metrics via JMX exporter javaagent
  • ServiceMonitor — Prometheus Operator integration for metrics collection
  • PodDisruptionBudget — optional disruption protection for cluster mode

Installation

HTTPS repository:

helm repo add helmforge https://repo.helmforge.dev
helm repo update
helm install kafka helmforge/kafka -f values.yaml

OCI registry:

helm install kafka oci://ghcr.io/helmforgedev/helm/kafka -f values.yaml

Topologies

ModePodsUse Case
single-broker1Development, CI, local testing
cluster (separate)3 controllers + 3 brokers = 6Production, full fault isolation
cluster (combined, brokers: 0)3 controllers acting as brokers = 3HA on limited nodes
Combined mode: 3-node HA with half the pods

Setting cluster.brokers.replicaCount: 0 enables combined mode where each controller pod also runs as a broker (process.roles=broker,controller). This provides full fault tolerance with only 3 pods instead of 6 — ideal for small clusters where you need HA but not the resource overhead of dedicated controller and broker StatefulSets.

Deployment Examples

# values.yaml — Single broker for development or lightweight use cases
architecture: single-broker

config:
  autoCreateTopicsEnabled: true # convenient for development
  logRetentionHours: 72

singleBroker:
  persistence:
    size: 10Gi
  resources:
    requests:
      memory: 512Mi
      cpu: 250m
    limits:
      memory: 2Gi
      cpu: 1000m
# values.yaml — 3 controller + 3 broker cluster for production
# Requires a stable KRaft Cluster ID — store it in a Secret before first deploy.
architecture: cluster

kraft:
  existingSecret: kafka-kraft-secret # contains kafka-cluster-id

config:
  numPartitions: 3
  autoCreateTopicsEnabled: false
  logRetentionHours: 168 # 7 days

cluster:
  minInSyncReplicas: 2
  controllers:
    replicaCount: 3
    persistence:
      size: 8Gi
    resources:
      requests:
        memory: 512Mi
        cpu: 250m
      limits:
        memory: 2Gi
        cpu: 1000m
  brokers:
    replicaCount: 3
    persistence:
      size: 50Gi
    resources:
      requests:
        memory: 1Gi
        cpu: 500m
      limits:
        memory: 4Gi
        cpu: 2000m

pdb:
  enabled: true
  minAvailable: 2
# values.yaml — 3-node combined mode (broker + controller on same pod)
# Each pod runs process.roles=broker,controller. Client service routes to controller pods.
architecture: cluster

kraft:
  existingSecret: kafka-kraft-secret

config:
  numPartitions: 3
  autoCreateTopicsEnabled: false
  logRetentionHours: 168

cluster:
  minInSyncReplicas: 2
  controllers:
    replicaCount: 3
    persistence:
      size: 30Gi # combines metadata + log storage
    resources:
      requests:
        memory: 1Gi
        cpu: 500m
      limits:
        memory: 4Gi
        cpu: 2000m
  brokers:
    replicaCount: 0 # 0 = combined mode, no separate broker StatefulSet

pdb:
  enabled: true
  minAvailable: 2
# values.yaml — Kafka cluster with Prometheus metrics
architecture: cluster

kraft:
  existingSecret: kafka-kraft-secret

cluster:
  minInSyncReplicas: 2
  controllers:
    replicaCount: 3
    persistence:
      size: 8Gi
  brokers:
    replicaCount: 3
    persistence:
      size: 50Gi

metrics:
  enabled: true
  serviceMonitor:
    enabled: true # requires Prometheus Operator
    interval: 30s
    labels:
      prometheus: kube-prometheus

pdb:
  enabled: true
  minAvailable: 2

Configuration Reference

Core

ParameterTypeDefaultDescription
architecturestringsingle-brokerBroker topology: single-broker or cluster.
nameOverridestring""Override the chart name.
fullnameOverridestring""Override the full release name.
commonLabelsobject{}Extra labels added to all resources.
clusterDomainstringcluster.localKubernetes cluster domain for internal DNS resolution.

Image

ParameterTypeDefaultDescription
image.repositorystringdocker.io/apache/kafkaKafka container image.
image.tagstring"4.2.0"Image tag.
image.pullPolicystringIfNotPresentImage pull policy.
imagePullSecretsarray[]Pull secrets for private registries.

KRaft Metadata

ParameterTypeDefaultDescription
kraft.existingSecretstring""Existing secret with KRaft Cluster ID (and controller directory IDs).
kraft.existingSecretClusterIdKeystringkafka-cluster-idKey in the existing secret for the Cluster ID.
kraft.existingSecretControllerDirectoryIdPrefixstringcontrollerPrefix for controller directory ID keys in the secret (e.g. controller-0-directory-id).
kraft.clusterIdstring""Explicit Cluster ID used when kraft.existingSecret is not set.
Fix the KRaft Cluster ID in production — do not let it regenerate

If the Cluster ID changes between deployments (e.g. during a Helm upgrade or reinstall), brokers with existing data from the previous Cluster ID will refuse to start. Always store the Cluster ID in a Kubernetes Secret (kraft.existingSecret) before the first deploy in production, and never delete that Secret while broker data persists on PVCs.

Listeners

ParameterTypeDefaultDescription
listeners.client.portinteger9092Client listener port for producers and consumers.
listeners.controller.portinteger9093KRaft controller listener port.
listeners.interBroker.portinteger9094Inter-broker listener port (cluster mode only).

Service

ParameterTypeDefaultDescription
service.typestringClusterIPClient bootstrap service type.
service.annotationsobject{}Annotations for the client Service.
service.labelsobject{}Extra labels for the client Service.

Configuration

ParameterTypeDefaultDescription
config.numPartitionsinteger3Default number of partitions per topic.
config.autoCreateTopicsEnabledbooleanfalseAllow brokers to create topics automatically on first produce.
config.deleteTopicEnabledbooleantrueAllow topic deletion via the admin API.
config.logRetentionHoursinteger168Default log retention period (7 days). Controls PVC growth for high-volume topics.
config.logSegmentBytesinteger1073741824Log segment size per partition (1 GB). Affects cleanup and compaction frequency.
config.commonstring""Extra configuration appended to every generated server.properties.
config.singleBrokerstring""Extra configuration appended in single-broker mode only.
config.controllerstring""Extra configuration appended only to controller pods in cluster mode.
config.brokerstring""Extra configuration appended only to broker pods in cluster mode.
autoCreateTopicsEnabled is false by default

Producers publishing to a non-existent topic will receive a UNKNOWN_TOPIC_OR_PARTITION error. Create topics explicitly before use, or set config.autoCreateTopicsEnabled: true in development environments. Keeping it false in production prevents accidental topic sprawl.

Single-Broker Topology

ParameterTypeDefaultDescription
singleBroker.persistence.enabledbooleantrueEnable PVC for single-broker data.
singleBroker.persistence.sizestring8GiPVC size for single-broker.
singleBroker.persistence.storageClassstring""StorageClass for single-broker PVC.
singleBroker.persistence.accessModesarray["ReadWriteOnce"]Access modes.
singleBroker.resourcesobject{}Resources for the single-broker container.

Cluster Topology

ParameterTypeDefaultDescription
cluster.minInSyncReplicasinteger2Minimum in-sync replicas for internal topics.
cluster.controllers.replicaCountinteger3Number of dedicated controller pods.
cluster.controllers.persistence.enabledbooleantrueEnable PVCs for controllers.
cluster.controllers.persistence.sizestring8GiPVC size per controller.
cluster.controllers.resourcesobject{}Resources for controller containers.
cluster.brokers.replicaCountinteger3Number of broker pods. Set to 0 for combined mode (brokers co-located on controllers).
cluster.brokers.persistence.enabledbooleantrueEnable PVCs for brokers.
cluster.brokers.persistence.sizestring20GiPVC size per broker.
cluster.brokers.resourcesobject{}Resources for broker containers.

PodDisruptionBudget

ParameterTypeDefaultDescription
pdb.enabledbooleanfalseCreate PodDisruptionBudgets for controllers and brokers.
pdb.minAvailableinteger1Minimum available pods during voluntary disruptions.

Metrics

ParameterTypeDefaultDescription
metrics.enabledbooleanfalseEnable JMX exporter javaagent for Prometheus metrics.
metrics.image.repositorystringdocker.io/bitnami/jmx-exporterJMX exporter init image repository.
metrics.image.tagstring"1.5.0"JMX exporter image tag.
metrics.portinteger5556Port exposed by the JMX exporter javaagent.
metrics.serviceMonitor.enabledbooleanfalseCreate Prometheus Operator ServiceMonitor resources.
metrics.serviceMonitor.intervalstring30sMetrics scrape interval.
metrics.serviceMonitor.labelsobject{}Labels added to the ServiceMonitor.

Security and Resources

Kafka runs as a non-root user (UID 1000) with read-write access to log directories. The fsGroup: 1000 ensures PVC data directories are writable by the Kafka process.

ParameterTypeDefaultDescription
podSecurityContext.fsGroupinteger1000Filesystem group for the pod.
securityContext.runAsUserinteger1000UID for the Kafka container process.
securityContext.runAsGroupinteger1000GID for the Kafka container process.
securityContext.runAsNonRootbooleantrueEnforce non-root execution.
securityContext.allowPrivilegeEscalationbooleanfalseDisallow privilege escalation.

Service Account

ParameterTypeDefaultDescription
serviceAccount.createbooleanfalseCreate a dedicated ServiceAccount.
serviceAccount.namestring""Override the ServiceAccount name.
serviceAccount.annotationsobject{}Annotations for the ServiceAccount.

Scheduling

ParameterTypeDefaultDescription
nodeSelectorobject{}Node selector for scheduling.
tolerationsarray[]Tolerations for scheduling.
affinityobject{}Affinity rules.
topologySpreadConstraintsarray[]Topology spread constraints.
priorityClassNamestring""PriorityClass for the pod.
terminationGracePeriodSecondsinteger120Termination grace period. Kafka needs time to drain producers and consumers.
podLabelsobject{}Extra labels for the pod.
podAnnotationsobject{}Extra annotations for the pod.

Extra

ParameterTypeDefaultDescription
extraEnvarray[]Extra environment variables for Kafka containers.
extraInitContainersarray[]Extra init containers.
extraVolumesarray[]Extra volumes to attach to the pod.
extraVolumeMountsarray[]Extra volume mounts for the container.
extraManifestsarray[]Extra Kubernetes manifests deployed alongside the chart.

More Information