A Kubernetes operator to manage Kafka resources declaratively.
Today, the operator manages Kafka topics: for every Topic object in the cluster, the operator creates and continuously
reconciles the corresponding topic in the target Kafka cluster (partitions, replication, topic configuration), and deletes the
topic again when the object is removed. Further Kafka resource types (such as ACLs or users) may be added in the future.
The operator does not run or manage Kafka brokers. It talks to an existing Kafka cluster through the Kafka admin API; the connection and authentication details are supplied per object through a referenced Kubernetes secret. That way, a single operator instance can manage topics across many different Kafka clusters.
API group/version: kafka.cs.sap.com/v1alpha1, kind Topic (namespaced).
apiVersion: kafka.cs.sap.com/v1alpha1
kind: Topic
metadata:
name: orders
namespace: my-namespace
spec:
connectionSecretRef:
name: my-kafka-connection
key: connection.yaml
name: orders.v1
partitions: 6
replicas: 3
configs:
retention.ms: "604800000"
cleanup.policy: compactReconciliation state is reported in the object's status:
status:
observedGeneration: 1
lastObservedAt: "2026-08-30T21:04:17Z"
state: Ready
conditions:
- type: Ready
status: "True"
reason: Synced
message: Topic synchronized successfully
lastTransitionTime: "2026-08-30T21:04:17Z"
observedGeneration: 1
details:
id: 9a1c2f...
name: orders.v1
partitions: 6
replicas: 3
configs:
cleanup.policy: "*compact"
retention.ms: "*604800000"
segment.bytes: "1073741824"The status fields are described in detail in Status fields below. Deletion of a Topic object deletes the
topic (and therefore all its data) in Kafka; the object is protected by a finalizer until the deletion has been confirmed by
the cluster.
The name of the topic in Kafka. If omitted, it defaults to metadata.name of the Topic object. The value must match
^[a-zA-Z0-9._-]{1,249}$.
The effective topic name is immutable. Once the object exists, the name can neither be changed nor be switched between the
explicit and the defaulted form in an incompatible way; the API server rejects such updates with spec.name is immutable.
Setting spec.name afterwards is only accepted if it equals metadata.name, and unsetting it is only accepted if the previous
value equaled metadata.name. To rename a topic, delete the object and create a new one (which destroys the topic's data).
The number of partitions; defaults to 1 if omitted.
Kafka only supports increasing the partition count, therefore the value may only be increased. Updates that lower it are
rejected with spec.partitions can only be increased; this also covers removing the field once it was set to a value greater
than 1 (because removing it would fall back to the default of 1).
The replication factor of the topic.
If omitted, the topic is created with the replication factor configured as default in the target Kafka cluster
(default.replication.factor), and the operator does not enforce any replication factor afterwards. If the field is set, the
operator actively aligns the partition assignments with the requested value, adding or removing replicas as needed.
Because the operator cannot reconstruct the cluster default retroactively, the field cannot be unset again once it was set;
such updates are rejected with spec.replicas cannot be unset. It can, however, be increased or decreased.
An optional map of Kafka topic configuration properties (such as retention.ms, cleanup.policy, min.insync.replicas, ...)
which override the corresponding broker defaults for this topic. Keys and values are passed to Kafka as-is, so all values have
to be given as strings.
The operator owns the set of overrides: entries added to spec.configs are set on the topic, changed entries are updated, and
entries removed from spec.configs are deleted again, so the topic falls back to the cluster default for that property.
Configuration properties which Kafka reports as sensitive are not supported.
The effective configuration of the topic is reported in status.details.configs; see Status fields below.
Required reference to the secret holding the connection details of the target Kafka cluster; see the next section.
The status is maintained exclusively by the operator; it is never to be edited by users.
An aggregated, human readable summary of the reconciliation state of the topic:
| State | Meaning |
|---|---|
Pending |
The topic could not be processed yet (e.g. the connection secret does not exist yet); the operator retries. |
InProgress |
The topic was just seen for the first time, or was created/updated and is being re-checked. |
Ready |
The topic exists in the Kafka cluster and matches the spec. |
Failed |
Reconciliation failed with a non-retriable error; see the Ready condition's message. |
Deleting |
The object is being deleted, and the topic deletion has been triggered in the Kafka cluster. |
Conditions in the standard Kubernetes format (type, status, reason, message, lastTransitionTime,
observedGeneration). Currently there is exactly one condition of type Ready, which is True only if the topic is fully
synchronized. Its reason is one of Unknown, FirstSeen, Processing, Synced, Deleting, Retrying, Error; in error
and retry cases, message carries the underlying error text. State and condition changes are additionally emitted as
Kubernetes events on the object (of type Warning in state Failed, Normal otherwise).
The metadata.generation of the object which was processed in the last reconciliation. As long as this value differs from
metadata.generation, the reported state refers to an outdated version of the spec. It is initialized with -1, meaning that
the object was not yet processed at all.
Timestamp of the last reconciliation of the object. Topics in state Ready are re-checked periodically (every 10 minutes), so
this timestamp also indicates how recent the reported details are.
The topic as it currently exists in the Kafka cluster (only populated in state Ready):
id: the Kafka topic ID (hex-encoded UUID) as assigned by the cluster.name: the effective topic name (that is,spec.name, ormetadata.nameifspec.nameis not set).partitions: the actual number of partitions of the topic.replicas: the actual replication factor of the topic. Note that this value can be zero: the replication factor is determined across all partitions, and if the partitions of the topic do not all have the same number of replicas (which can happen after failed or partial reassignments, or if the topic was modified outside of the operator), then no single replication factor exists, and0is reported instead.configs: the topic configuration.
status.details.configs contains all configuration properties of the topic as reported by the cluster — not only the ones
listed in spec.configs, but also all properties inherited from the broker/cluster defaults. Properties which are overridden
on topic level (i.e. those set through spec.configs) are marked with a leading asterisk prepended to their value. In the
example above, cleanup.policy ("*compact") and retention.ms ("*604800000") are topic-level overrides with the effective
values compact and 604800000, while segment.bytes ("1073741824") is inherited from the cluster defaults. The asterisk
is a marker only; it is not part of the actual configuration value.
spec.connectionSecretRef points to a secret in the same namespace as the Topic object:
spec:
connectionSecretRef:
# name of the secret (required)
name: my-kafka-connection
# key inside the secret's data (optional, defaults to "value")
key: connection.yamlIf key is omitted, the operator falls back to the key value. If neither the secret nor the key exists, reconciliation is
retried (state Pending) until the secret shows up.
The referenced key must contain a YAML (or JSON, since JSON is valid YAML) document with the following structure:
# list of seed brokers, at least one entry is required
brokers:
- kafka-0.example.com:9093
- kafka-1.example.com:9093
# PEM-encoded CA bundle used to verify the broker certificates (optional)
caBundle: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
# PEM-encoded client certificate for mTLS (optional)
clientCertificate: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
# PEM-encoded private key belonging to clientCertificate (optional)
clientKey: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
# SASL mechanisms (optional)
sasl:
- scramSha512:
username: my-user
password: my-passwordRules enforced when parsing the connection details:
brokersmust contain at least one entry; entries have the usualhost:portsyntax.- TLS is enabled as soon as at least one of
caBundle,clientCertificate,clientKeyis provided; the minimum TLS version is 1.2. If none of them is set, the operator connects in plaintext. WithoutcaBundle, the system trust store is used. clientCertificateandclientKeymust be specified together (client/mutual TLS authentication).saslis a list of mechanism entries; each entry must specify exactly one ofplain,scramSha256orscramSha512, and the chosen mechanism must have bothusernameandpasswordset.
Example secret:
apiVersion: v1
kind: Secret
metadata:
name: my-kafka-connection
namespace: my-namespace
stringData:
connection.yaml: |
brokers:
- kafka-0.example.com:9093
caBundle: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
sasl:
- scramSha512:
username: my-user
password: my-passwordSince the connection secret is looked up in the namespace of the Topic object, the topics of a given Kafka cluster can be
managed from any namespace which holds a suitable secret.
The recommended deployment method is to use the Helm chart:
helm upgrade -i kafka-operator oci://ghcr.io/sap/kafka-operator-helm/kafka-operatorThe API reference is here: https://pkg.go.dev/github.com/sap/kafka-operator.
This project is open to feature requests/suggestions, bug reports etc. via GitHub issues. Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our Contribution Guidelines.
If you find any bug that may be a security problem, please follow our instructions at in our security policy on how to report it. Please do not create GitHub issues for security-related doubts or problems.
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone. By participating in this project, you agree to abide by its Code of Conduct at all times.
Copyright 2026 SAP SE or an SAP affiliate company and kafka-operator contributors. Please see our LICENSE for copyright and license information. Detailed information including third-party components and their licensing/copyright information is available via the REUSE tool.