ClickHouseCluster configuration
Basic configuration
Replicas and shards
- Replicas: Number of ClickHouse instances per shard (for high availability)
- Shards: Number of horizontal partitions (for scaling)
replicas: 3 and shards: 2 will create 6 ClickHouse pods total.
Keeper integration
Every ClickHouse cluster must reference a KeeperCluster for coordination:keeperClusterRef.namespace is set, the operator must watch both namespaces. If WATCH_NAMESPACE is configured, include the ClickHouse and Keeper namespaces in that list.
KeeperCluster configuration
Storage configuration
Configure persistent storage withdataVolumeClaimSpec, a standard Kubernetes
PersistentVolumeClaimSpec. The operator turns it into a per-replica PersistentVolumeClaim
mounted at the data path /var/lib/clickhouse:
The operator can modify an existing PVC only if the underlying StorageClass supports volume expansion.
Cluster domain
spec.clusterDomain sets the Kubernetes DNS suffix the operator uses when it builds
the fully-qualified pod host names it writes into the ClickHouse server
configuration. It defaults to cluster.local and exists on both
ClickHouseCluster and KeeperCluster.
<pod>.<headless-service>.<namespace>.svc.<clusterDomain>. That suffix flows into
two parts of the generated configuration:
- On a
ClickHouseCluster, its value is used for the replica host names inremote_servers(cross-replica andDistributedqueries). - On a
KeeperCluster, its value builds the Keeper node host names that ClickHouse uses for coordination.
Only override this when your cluster’s
kubelet runs with a --cluster-domain
other than cluster.local. If the value does not match the real cluster domain,
ClickHouse cannot resolve the Keeper and replica host names — coordination and
Distributed queries fail with DNS resolution errors. Set the same value on the
ClickHouseCluster and the KeeperCluster it references.Multi-disk (JBOD) storage
additionalVolumeClaimTemplates attaches extra disks to each ClickHouse replica, on top of the primary dataVolumeClaimSpec, which is required to use them.
Each entry is a PVC template — a metadata.name plus a PVC spec.
The disks are reconciled exactly like the primary data disk — as StatefulSet volumeClaimTemplates — so the StatefulSet controller creates and retains one PVC per replica, named <name>-<statefulset>-0.
/var/lib/clickhouse/disks/<name> and adds it to a generated ClickHouse storage configuration.
Hyphens in a name become underscores in the ClickHouse disk identifier; the mount path keeps the original name.
The primary data disk and every additional disk are placed in a single volume of the default storage policy, so ClickHouse spreads new data parts across all of them in round-robin fashion.
Usable capacity is the sum of all disks, and every table that does not set its own storage_policy (including system.* tables) uses the combined set.
PVC names must match
^[a-z]([-a-z0-9]*[a-z0-9])?$ and must not collide with the primary data volume name.
Like the primary data disk, the set of additional disks is fixed at creation: adding, removing, or renaming entries after creation is rejected.
Additional PVCs are retained when the cluster is deleted, like the primary data disk.
Storage size on an existing entry can be expanded if the StorageClass supports expansion.Cluster domain
spec.clusterDomain sets the Kubernetes DNS suffix the operator uses when it builds
the fully-qualified pod host names it writes into the ClickHouse server
configuration. It defaults to cluster.local and exists on both
ClickHouseCluster and KeeperCluster.
<pod>.<headless-service>.<namespace>.svc.<clusterDomain>. That suffix flows into
two parts of the generated configuration:
- On a
ClickHouseCluster, its value is used for the replica host names inremote_servers(cross-replica andDistributedqueries). - On a
KeeperCluster, its value builds the Keeper node host names that ClickHouse uses for coordination.
Only override this when your cluster’s
kubelet runs with a --cluster-domain
other than cluster.local. If the value does not match the real cluster domain,
ClickHouse cannot resolve the Keeper and replica host names — coordination and
Distributed queries fail with DNS resolution errors. Set the same value on the
ClickHouseCluster and the KeeperCluster it references.Pod configuration
Automatic topology spread and affinity
Distribute pods across availability zones:Ensure your Kubernetes cluster has enough nodes in different zones to satisfy the spread constraints.
Manual configuration
Arbitrary pod affinity/anti-affinity rules and topology spread constraints can be specified.See API Reference for all supported Pod template options.
Pod disruption budgets
The operator creates a PodDisruptionBudget (PDB) for each cluster so that voluntary disruptions — node drains, rolling upgrades, autoscaler evictions — cannot take down enough pods to lose quorum or break availability. For ClickHouse clusters with more than one shard, one PDB is created per shard so a disruption in one shard cannot count against another.Defaults
The operator picks safe defaults based on the cluster size so that a freshapply already protects against accidental quorum loss.
For a 3-shard ClickHouseCluster with
replicas: 3, the operator creates three PDBs, one per shard, each with minAvailable: 1.
Overriding the defaults
Usespec.podDisruptionBudget to override either minAvailable or maxUnavailable (exactly one):
maxUnavailable form, with a percentage:
unhealthyPodEvictionPolicy field through to the generated PDB — useful when you need to allow eviction of pods that are still in NotReady:
Policies
spec.podDisruptionBudget.policy lets you choose how aggressively the operator manages PDBs:
Example — disable PDB management completely on a development cluster:
Cluster-wide opt-out
PDB management can also be disabled cluster-wide via the operator’sENABLE_PDB environment variable. With ENABLE_PDB=false, the operator skips the PDB reconcile step for every ClickHouseCluster and KeeperCluster regardless of their spec.podDisruptionBudget.policy, and does not watch PodDisruptionBudget resources at all. The operator’s ServiceAccount therefore does not need RBAC permissions on poddisruptionbudgets.policy/v1, which is useful when running the operator under a restricted ServiceAccount that intentionally omits those permissions.
Container configuration
Custom image
Use a specific ClickHouse image:Container resources
Configure CPU and memory for ClickHouse containers:Environment variables
Add custom environment variables:Volume mounts
Add additional volume mounts:It is allowed to specify multiple volume mounts to the same
mountPath.
Operator will create projected volume with all specified mounts.See API Reference for all supported Container template options.
TLS/SSL configuration
Configure secure endpoints
Pass a reference to a Kubernetes Secret containing TLS certificates to enable secure endpointsSSL certificate secret format
It is expected that the Secret contains the server keypair:tls.crt- PEM encoded server certificatetls.key- PEM encoded private key
This format is compatible with cert-manager generated certificates.
ClickHouse-Keeper communication over TLS
If KeeperCluster has TLS enabled, ClickHouseCluster would use secure connection to Keeper nodes automatically. ClickHouseCluster verifies Keeper node certificates against the system trust store, plus anycaBundle you configure.
To trust a private CA (for example, a self-signed or internal CA), provide a custom CA bundle reference:
External Secret
By default the operator creates and owns a Secret containing the cluster’s internal credentials (interserver password, management password, keeper identity, cluster secret, named-collections key). The Secret is named after the cluster and lives in the cluster’s namespace. If you want to manage these credentials yourself — for example, sourcing them from HashiCorp Vault, AWS Secrets Manager, or External Secrets Operator — point the operator at a pre-existing Secret usingspec.externalSecret:
The referenced Secret must reside in the same namespace as the ClickHouseCluster. The operator never deletes a Secret it did not create.
Required keys
The Secret must contain the following keys:
A complete Secret looks like this:
Policy: Observe vs Manage
spec.externalSecret.policy controls how the operator handles missing required keys:
Even with
policy: Manage the Secret must already exist in the namespace — the operator never creates the Secret itself, it only writes generated keys into an existing one. If the referenced Secret is missing, reconciliation is blocked with the ExternalSecretNotFound reason regardless of policy.Observe when an external system (Vault, ESO, sealed-secrets, GitOps) is the source of truth and you want the operator to fail loudly on misconfiguration. Pick Manage when you want self-sufficient bootstrapping but still want to retain ownership of the Secret object itself (for example, to back it up).
Status condition and troubleshooting
The operator exposes aExternalSecretValid condition on ClickHouseCluster.status.conditions. Inspect it when reconciliation looks stuck:
The operator requeues reconciliation while the Secret is invalid, so once you add the missing keys the next reconcile picks them up automatically — no need to bounce pods.
The set of required keys depends on the running ClickHouse version.
named-collections-key is only validated once the operator’s version probe has detected ClickHouse 25.12 or newer. On older versions the key may be absent from the Secret.Additional ports
The operator exposes a fixed set of ports on every ClickHouse Pod and its headless Service:8123 HTTP, 9000 native, 9009 interserver, 9001 management, 9363 Prometheus metrics, and the TLS variants 8443/9440 when TLS is enabled. To make ClickHouse listen on additional protocols — MySQL, PostgreSQL, gRPC, or any custom port — declare them in spec.additionalPorts:
containerPorts and to the headless Service. The complete example lives at examples/custom_protocols.yaml.
End-to-end example: MySQL wire protocol
To expose ClickHouse over the MySQL wire protocol on port9004:
Field constraints
Reserved ports and names
The validating webhook rejectsadditionalPorts entries that would collide with ports the operator binds itself. All TLS-related ports are reserved unconditionally so that flipping spec.settings.tls.enabled later cannot break a previously valid cluster.
The following names are also rejected — they are the operator’s internal protocol-type identifiers (not the human-readable aliases):
A rejected request produces an error such as:
Version probe and upgrade channel
The operator does two independent things with cluster versions:- Version reporting — for
ClickHouseCluster, a KubernetesJobruns the container image once to detect the running ClickHouse version; forKeeperCluster, the operator reads the server-reported version from running replicas. The detected version is recorded in.status.versionand used by other reconciliation steps (e.g. theExternal Secretnamed-collections key is only required from ClickHouse25.12). - Upgrade channel — a periodic check against the public ClickHouse release feed (
https://clickhouse.com/data/version_date.tsv). The operator reports whether a newer version is available via theVersionUpgradedstatus condition. It never upgrades the cluster on its own — the user is in control of the image tag.
Choosing a release channel
spec.upgradeChannel selects which set of upstream releases the operator compares against. Same field exists on both ClickHouseCluster and KeeperCluster.
^(lts|stable|\d+\.\d+)?$):
For production, pinning the channel to an explicit
<major>.<minor> (e.g. 25.8) is generally preferred. It locks the cluster to the intended major release line and lets the operator surface a WrongReleaseChannel warning if any replica somehow drifts onto a different major — which matters especially when the image is referenced by a digest (@sha256:...) rather than by a human-readable tag. The empty default is fine for development clusters where major-version jumps are not a concern.
Status conditions
Two conditions surface the result of the probe and the upgrade check:
Inspect them with:
Overriding the version probe Job
This applies toClickHouseCluster only. KeeperCluster no longer runs a version-probe Job — its version is read directly from the running Keeper replicas — so spec.versionProbeTemplate is deprecated and has no effect there.
The probe is implemented as a regular Kubernetes Job. If your cluster has admission policies that require specific Tolerations, node selectors, security contexts, or you want to limit how long completed probe Jobs linger, override the template via spec.versionProbeTemplate:
version-probe is the operator’s default — the entry under containers: matches it by name, so the operator deep-merges the user-provided fields on top of the defaults.
Operator-wide controls
Two flags on the operator manager control the upgrade-check loop globally:
Set
--disable-version-update-checks=true in air-gapped environments or when egress to clickhouse.com is not allowed.
ClickHouse settings
Default user password
spec.settings.defaultUserPassword sets the password for the built-in default
user. Provide the value from a key in a Secret (recommended) or a ConfigMap that
you create, rather than inline in the CR:
secret or configMap, each with both name (the object)
and key (the entry that holds the password).
Password types
passwordType tells ClickHouse how to interpret the value. It defaults to
password (plaintext); the alternatives are hashed forms such as
password_sha256_hex and password_double_sha1_hex. Prefer a hashed type so the
plaintext is never stored. See the
ClickHouse user settings
for the full list.
Full example with a Secret
Create the Secret, then reference its key:With
passwordType: password, the in-pod clickhouse-client is configured with
this password, which is handy for debugging.Using a ConfigMap
A ConfigMap works the same way, but its contents are not protected like a Secret. Use it only for non-sensitive or already-hashed values, such as apassword_sha256_hex digest:
Do not put a plaintext password in a ConfigMap. Use a Secret for any plaintext
(
passwordType: password) value.Custom users in configuration
Configure additional users in configuration files. Create a ConfigMap and Secret for user:Database sync
Enable automatic database synchronization for new replicas:Server logging
Configure the ClickHouse server log throughspec.settings.logger. Every field is optional with a safe default, so a cluster you never touch already logs at trace to both the container console and a rotated file on disk.
The operator always keeps console logging on so that
kubectl logs works, and layers file logging on top when logToFile is true. A cluster with the defaults renders this logger block:
spec.settings.logger block applies to a KeeperCluster; the operator writes its files under /var/log/clickhouse-keeper/ instead.
Console logging stays on regardless of
logToFile, so kubectl logs keeps working even when you disable file logging. Set jsonLogs: true when you ship logs to a structured log store that parses JSON.Custom configuration
Embedded extra configuration
Instead of mounting custom configuration files, you can directly specify additional ClickHouse configuration options. Add custom ClickHouse configuration usingextraConfig:
Useful links:
Embedded extra users configuration
You can also specify additional ClickHouse users configuration usingextraUsersConfig. This is useful for defining users, profiles, quotas, and grants directly in the cluster specification.
The
extraUsersConfig is stored in k8s ConfigMap object. Avoid plain text secrets there.