> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-home-button.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# ClickHouse Operator configuration guide

> This guide covers how to configure ClickHouse and Keeper clusters using the ClickHouse operator.

This guide covers how to configure ClickHouse and Keeper clusters using the operator.

<h2 id="clickhousecluster-configuration">
  ClickHouseCluster configuration
</h2>

<h3 id="basic-configuration">
  Basic configuration
</h3>

```yaml theme={null}
apiVersion: clickhouse.com/v1alpha1
kind: ClickHouseCluster
metadata:
  name: my-cluster
spec:
  replicas: 3           # Number of replicas per shard
  shards: 2             # Number of shards
  keeperClusterRef:
    name: my-keeper     # Reference to KeeperCluster
  dataVolumeClaimSpec:
    resources:
      requests:
        storage: 10Gi
```

<h3 id="replicas-and-shards">
  Replicas and shards
</h3>

* **Replicas**: Number of ClickHouse instances per shard (for high availability)
* **Shards**: Number of horizontal partitions (for scaling)

```yaml theme={null}
spec:
  replicas: 3  # Default: 3
  shards: 2    # Default: 1
```

A cluster with `replicas: 3` and `shards: 2` will create 6 ClickHouse pods total.

<h3 id="keeper-integration">
  Keeper integration
</h3>

Every ClickHouse cluster must reference a KeeperCluster for coordination:

```yaml theme={null}
spec:
  keeperClusterRef:
    name: my-keeper
    # namespace: keeper-system  # Optional, defaults to the ClickHouseCluster namespace
```

When `keeperClusterRef.namespace` is set, the operator must watch both namespaces. If `WATCH_NAMESPACE` is configured, include the ClickHouse and Keeper namespaces in that list.

<h2 id="keepercluster-configuration">
  KeeperCluster configuration
</h2>

```yaml theme={null}
apiVersion: clickhouse.com/v1alpha1
kind: KeeperCluster
metadata:
  name: my-keeper
spec:
  replicas: 3  # Must be odd: 1, 3, 5, 7, 9, 11, 13, or 15
  dataVolumeClaimSpec:
    resources:
      requests:
        storage: 5Gi
```

<h2 id="storage-configuration">
  Storage configuration
</h2>

Configure persistent storage:

```yaml theme={null}
spec:
  dataVolumeClaimSpec:
    storageClassName: fast-ssd  # Optional: consider your storage class based on the installed CSI
    resources:
      requests:
        storage: 100Gi
```

<Note>
  Operator can modify existing PVC only if the underlying storage class supports volume expansion.
</Note>

<h2 id="pod-configuration">
  Pod configuration
</h2>

<h3 id="automatic-topology-spread-and-affinity">
  Automatic topology spread and affinity
</h3>

Distribute pods across availability zones:

```yaml theme={null}
spec:
  podTemplate:
    topologyZoneKey: topology.kubernetes.io/zone
    nodeHostnameKey: kubernetes.io/hostname
```

<Note>
  Ensure your Kubernetes cluster has enough nodes in different zones to satisfy the spread constraints.
</Note>

<h3 id="manual-configuration">
  Manual configuration
</h3>

Arbitrary pod affinity/anti-affinity rules and topology spread constraints can be specified.

```yaml theme={null}
spec:
  podTemplate:
    affinity:
      <your-affinity-rules-here>
    topologySpreadConstraints:
      <your-topology-spread-constraints-here>
```

### See [API Reference](/products/kubernetes-operator/reference/api-reference#podtemplatespec) for all supported Pod template options.

<h2 id="pod-disruption-budgets">
  Pod disruption budgets
</h2>

The operator creates a [PodDisruptionBudget](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/) (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.

<h3 id="pdb-defaults">
  Defaults
</h3>

The operator picks safe defaults based on the cluster size so that a fresh `apply` already protects against accidental quorum loss.

| Resource            | Topology                             | Default PDB                                                                                                                            |
| ------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `ClickHouseCluster` | `replicas: 1` (single-replica shard) | `maxUnavailable: 1` — disruption is allowed for a single-node cluster so that node drains are not blocked                              |
| `ClickHouseCluster` | `replicas: 2+` (multi-replica shard) | `minAvailable: 1` — at least one replica per shard must stay up                                                                        |
| `KeeperCluster`     | `replicas: 1`                        | `maxUnavailable: 1` — disruption is allowed for a single-node cluster so that node drains are not blocked                              |
| `KeeperCluster`     | `replicas: 3+`                       | `maxUnavailable: replicas/2` — preserves the RAFT quorum for a `2F+1` cluster (3 replicas tolerate 1 down, 5 replicas tolerate 2 down) |

For a 3-shard ClickHouseCluster with `replicas: 3`, the operator creates three PDBs, one per shard, each with `minAvailable: 1`.

<h3 id="pdb-overrides">
  Overriding the defaults
</h3>

Use `spec.podDisruptionBudget` to override either `minAvailable` **or** `maxUnavailable` (exactly one):

```yaml theme={null}
spec:
  replicas: 3
  shards: 2
  podDisruptionBudget:
    minAvailable: 2   # keep at least 2 of 3 replicas in every shard up during a disruption
```

Or the `maxUnavailable` form, with a percentage:

```yaml theme={null}
spec:
  replicas: 5
  podDisruptionBudget:
    maxUnavailable: 40%
```

<Warning>
  Setting both `minAvailable` and `maxUnavailable` is rejected by the validating webhook. Pick one — Kubernetes itself does not allow both either.
</Warning>

You can also pass the [`unhealthyPodEvictionPolicy`](https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#unhealthy-pod-eviction-policy) field through to the generated PDB — useful when you need to allow eviction of pods that are still in `NotReady`:

```yaml theme={null}
spec:
  podDisruptionBudget:
    minAvailable: 2
    unhealthyPodEvictionPolicy: AlwaysAllow
```

<h3 id="pdb-policies">
  Policies
</h3>

`spec.podDisruptionBudget.policy` lets you choose **how aggressively** the operator manages PDBs:

| Policy              | Behavior                                                                                                                                                                          |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Enabled` (default) | The operator creates and updates the PDB on every reconcile. This is the safe production default.                                                                                 |
| `Disabled`          | The operator does **not** create PDBs and **deletes** any existing ones with matching labels. Useful for development clusters where every voluntary disruption should be allowed. |
| `Ignored`           | The operator neither creates nor deletes PDBs. Existing PDBs are left alone. Use this when another system (e.g. policy admission, GitOps tool) owns PDB management for you.       |

Example — disable PDB management completely on a development cluster:

```yaml theme={null}
spec:
  podDisruptionBudget:
    policy: Disabled
```

Example — keep your hand-crafted PDB next to the cluster and stop the operator from touching it:

```yaml theme={null}
spec:
  podDisruptionBudget:
    policy: Ignored
```

<h3 id="pdb-cluster-wide-disable">
  Cluster-wide opt-out
</h3>

PDB management can also be disabled cluster-wide via the operator's `ENABLE_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.

```yaml theme={null}
# in the operator Deployment spec
env:
- name: ENABLE_PDB
  value: "false"
```

This is intended for environments that ship their own disruption policies (e.g. through Gatekeeper / Kyverno) and want the operator out of the loop entirely.

<h2 id="container-configuration">
  Container configuration
</h2>

<h3 id="custom-image">
  Custom image
</h3>

Use a specific ClickHouse image:

```yaml theme={null}
spec:
  containerTemplate:
    image:
      repository: clickhouse/clickhouse-server
      tag: "25.12"
    imagePullPolicy: IfNotPresent
```

<h3 id="container-resources">
  Container resources
</h3>

Configure CPU and memory for ClickHouse containers:

```yaml theme={null}
# default values
spec:
  containerTemplate:
    resources:
      requests:
        cpu: "250m"
        memory: "256Mi"
      limits:
        cpu: "1"
        memory: "1Gi"
```

<h3 id="environment-variables">
  Environment variables
</h3>

Add custom environment variables:

```yaml theme={null}
spec:
  containerTemplate:
    env:
    - name: CUSTOM_ENV_VAR
      value: "1"
```

<h3 id="volume-mounts">
  Volume mounts
</h3>

Add additional volume mounts:

```yaml theme={null}
spec:
  containerTemplate:
    volumeMounts:
    - name: custom-config
      mountPath: /etc/clickhouse-server/config.d/custom.xml
      subPath: custom.xml
```

<Note>
  It is allowed to specify multiple volume mounts to the same `mountPath`.
  Operator will create projected volume with all specified mounts.
</Note>

### See [API Reference](/products/kubernetes-operator/reference/api-reference#containertemplatespec) for all supported Container template options.

<h2 id="tls-ssl-configuration">
  TLS/SSL configuration
</h2>

<h3 id="configure-secure-endpoints">
  Configure secure endpoints
</h3>

Pass a reference to a Kubernetes Secret containing TLS certificates to enable secure endpoints

```yaml theme={null}
spec:
  settings:
    tls:
      enabled: true
      required: true # Insecure ports are disabled if set
      serverCertSecret:
        name: <certificate-secret-name>
```

<h3 id="ssl-certificate-secret-format">
  SSL certificate secret format
</h3>

It is expected that the Secret contains the following keys:

* `tls.crt` - PEM encoded server certificate
* `tls.key` - PEM encoded private key
* `ca.crt` - PEM encoded CA certificate chain

<Note>
  This format is compatible with cert-manager generated certificates.
</Note>

<h3 id="clickhouse-keeper-communication-over-tls">
  ClickHouse-Keeper communication over TLS
</h3>

If KeeperCluster has TLS enabled, ClickHouseCluster would use secure connection to Keeper nodes automatically.

ClickHouseCluster should be able to verify Keeper nodes certificates.
If ClickHouseCluster has TLS enabled, is uses `ca.crt` bundle for verification. Otherwise, default CA bundle is used.

User may provide a custom CA bundle reference:

```yaml theme={null}
spec:
    settings:
        tls:
          caBundle:
            name: <ca-certificate-secret-name>
            key: <ca-certificate-key>
```

<h2 id="clickhouse-settings">
  ClickHouse settings
</h2>

<h3 id="default-user-password">
  Default user password
</h3>

Set the default user password:

```yaml theme={null}
spec:
  settings:
    defaultUserPassword:
      passwordType: <password-type> # Default: password
      <secret|configMap>:
        name: <resource name>
        key: <password>
```

<Note>
  It isn't recommended to use ConfigMap to store plain text passwords.
</Note>

Create the secret:

```bash theme={null}
kubectl create secret generic clickhouse-password --from-literal=password='your-secure-password'
```

<h4 id="using-configmap-for-user-passwords">
  Using ConfigMap for user passwords
</h4>

You can also use ConfigMap for non-sensitive default passwords:

```yaml theme={null}
spec:
  settings:
    defaultUserPassword:
      passwordType: password_sha256_hex
      configMap:
        name: clickhouse-config
        key: default_password
```

<h3 id="custom-users-in-configuration">
  Custom users in configuration
</h3>

Configure additional users in configuration files.

Create a ConfigMap and Secret for user:

```yaml theme={null}
apiVersion: v1
kind: ConfigMap
metadata:
  name: user-config
data:
  reader.yaml: |
    users:
      reader:
        password:
          - '@from_env': READER_PASSWORD
        profile: default
        grants:
          - query: "GRANT SELECT ON *.*"
---
apiVersion: v1
kind: Secret
metadata:
  name: reader-password
data:
  password: "c2VjcmV0LXBhc3N3b3Jk"  # base64("secret-password")

```

Add custom configuration to ClickHouseCluster:

```yaml theme={null}
spec:
  podTemplate:
    volumes:
      - name: reader-user
        configMap:
          name: user-config
  containerTemplate:
    env:
      - name: READER_PASSWORD
        valueFrom:
          secretKeyRef:
            name: reader-password
            key: password
    volumeMounts:
      - mountPath: /etc/clickhouse-server/users.d/
        name: reader-user
        readOnly: true
```

<h3 id="database-sync">
  Database sync
</h3>

Enable automatic database synchronization for new replicas:

```yaml theme={null}
spec:
  settings:
    enableDatabaseSync: true  # Default: true
```

When enabled, the operator synchronizes Replicated and integration tables to new replicas.

<h2 id="custom-configuration">
  Custom configuration
</h2>

<h3 id="embedded-extra-configuration">
  Embedded extra configuration
</h3>

Instead of mounting custom configuration files, you can directly specify additional ClickHouse configuration options.

Add custom ClickHouse configuration using `extraConfig`:

```yaml theme={null}
spec:
  settings:
    extraConfig:
      background_pool_size: 20
```

#### Useful links:

* [YAML configuration examples](/concepts/features/configuration/server-config/configuration-files#example-1)
* [All server settings](/reference/settings/server-settings/settings)

<h3 id="embedded-extra-users-configuration">
  Embedded extra users configuration
</h3>

You can also specify additional ClickHouse users configuration using `extraUsersConfig`. This is useful for defining users, profiles, quotas, and grants directly in the cluster specification.

```yaml theme={null}
spec:
  settings:
    extraUsersConfig:
      users:
        analyst:
          password:
            - '@from_env': ANALYST_PASSWORD
          profile: "readonly"
          quota: "default"
      profiles:
        readonly:
          readonly: 1
          max_memory_usage: 10000000000
      quotas:
        default:
          interval:
            duration: 3600
            queries: 1000
            errors: 100
```

<Note>
  The `extraUsersConfig` is stored in k8s ConfigMap object. Avoid plain text secrets there.
</Note>

#### See [documentation](/concepts/features/configuration/settings/settings-users) for all supported ClickHouse users configuration options.

<h3 id="configuration-example">
  Configuration example
</h3>

Complete configuration example:

```yaml theme={null}
apiVersion: clickhouse.com/v1alpha1
kind: KeeperCluster
metadata:
  name: sample
spec:
  replicas: 3
  dataVolumeClaimSpec:
    storageClassName: <storage-class-name>
    resources:
      requests:
        storage: 10Gi
  podTemplate:
    topologyZoneKey: topology.kubernetes.io/zone
    nodeHostnameKey: kubernetes.io/hostname
  containerTemplate:
    resources:
      requests:
        cpu: "2"
        memory: "4Gi"
      limits:
        cpu: "4"
        memory: "8Gi"
  settings:
    tls:
      enabled: true
      required: true
      serverCertSecret:
        name: <keeper-certificate-secret>
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: default-user-password
data:
  # secret-password
  password: "..." # sha256 hex of the password
---
apiVersion: clickhouse.com/v1alpha1
kind: ClickHouseCluster
metadata:
  name: sample
spec:
  replicas: 2
  dataVolumeClaimSpec:
    storageClassName: <storage-class-name>
    resources:
      requests:
        storage: 200Gi
  keeperClusterRef:
    name: sample
  podTemplate:
    topologyZoneKey: topology.kubernetes.io/zone
    nodeHostnameKey: kubernetes.io/hostname
  settings:
    tls:
      enabled: true
      required: true
      serverCertSecret:
        name: clickhouse-cert
    defaultUserPassword:
      passwordType: password_sha256_hex
      configMap:
        key: password
        name: default-password
```
