Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Storage and volumes

This guide covers how the operator provisions persistent storage for a ClickHouseCluster: the primary data volume, attaching extra disks in a multi-disk (JBOD) layout, expanding capacity, and the rules that govern what you can and cannot change after a cluster exists.

For the field-by-field reference, see Configuration → Storage configuration and the API Reference.

Primary data volume

spec.dataVolumeClaimSpec is a standard Kubernetes PersistentVolumeClaimSpec. The operator turns it into a StatefulSet volumeClaimTemplate, so the StatefulSet controller creates and retains one PersistentVolumeClaim per replica and mounts it at the ClickHouse data path /var/lib/clickhouse.

apiVersion: clickhouse.com/v1alpha1
kind: ClickHouseCluster
metadata:
  name: my-cluster
spec:
  dataVolumeClaimSpec:
    storageClassName: fast-ssd   # optional; depends on the installed CSI driver
    resources:
      requests:
        storage: 100Gi
  • When accessModes is omitted, the operator defaults it to ReadWriteOnce.
  • The per-replica PVC is retained when the cluster is deleted, so data survives a delete-and-recreate of the Custom Resource. For data on an encrypted policy this additionally requires preserving the encryption key — see the note in that section.
  • The same field exists on KeeperCluster and behaves the same way.

Running without a persistent data volume

dataVolumeClaimSpec is optional. If you omit it and do not mount your own volume at the data path, ClickHouse writes to the container’s ephemeral filesystem and the admission webhook returns a warning that data may be lost if the cluster is restarted.

This is intended only for throwaway or test clusters. To supply your own storage instead of dataVolumeClaimSpec — for example an emptyDir or a pre-provisioned volume — define it through spec.podTemplate.volumes and mount it at /var/lib/clickhouse with spec.containerTemplate.volumeMounts.

Expanding storage

To grow a volume, increase resources.requests.storage and apply the change. The operator updates the existing PVCs in place.

spec:
  dataVolumeClaimSpec:
    resources:
      requests:
        storage: 200Gi   # was 100Gi

Multi-disk (JBOD) storage

spec.additionalVolumeClaimTemplates attaches extra disks to each ClickHouse replica on top of the primary dataVolumeClaimSpec. Each entry is a named PVC template — a metadata.name plus a PVC spec — reconciled exactly like the primary data disk, so the StatefulSet controller creates and retains one PVC per replica named <name>-<statefulset>-0.

spec:
  dataVolumeClaimSpec:
    storageClassName: fast-ssd
    resources:
      requests:
        storage: 100Gi
  additionalVolumeClaimTemplates:
    - metadata:
        name: disk1
      spec:
        storageClassName: fast-ssd
        resources:
          requests:
            storage: 100Gi
    - metadata:
        name: disk2
      spec:
        storageClassName: fast-ssd
        resources:
          requests:
            storage: 100Gi

The operator mounts each additional volume at /var/lib/clickhouse/disks/<name> and generates the ClickHouse storage_configuration for you — you do not write it by hand. It registers every additional disk and adds it to the built-in default storage policy.

The primary data disk (default) and every additional disk share a single volume of the default 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.

Custom storage policies

You do not need extraConfig for the JBOD layout above — the operator generates the default policy automatically. Reach for spec.settings.extraConfig only when you want storage policies beyond the generated default, for example a tiered hot/cold policy with move_factor and prefer_not_to_merge, or an S3-backed disk. Configuration you add there is merged on top of the generated storage_configuration.

See the ClickHouse storage documentation for the policy fields.

At-rest encryption

Setting spec.settings.encryption enables at-rest encryption of table data. The operator generates a 16-byte AES key — stored in the managed cluster Secret, or supplied through externalSecret — and a dedicated storage policy that wraps every data disk with ClickHouse’s encrypted disk type.

spec:
  settings:
    encryption: {}   # enables the feature; the policy defaults to "encrypted"

Encryption is opt in per table; the default storage policy stays plaintext. Select the encrypted policy when creating a table:

CREATE TABLE secret_data (id UInt64) ENGINE = MergeTree ORDER BY id
SETTINGS storage_policy = 'encrypted';

Set encryption.policyName to use a different policy name.

What you cannot change after creation

Storage layout is largely fixed once a cluster exists. Updates that would orphan data or rebind PersistentVolumeClaims are rejected at admission:

  • The presence of dataVolumeClaimSpec is immutable — you cannot add a data volume to a cluster created without one, nor remove it from a cluster created with one.
  • The set of additionalVolumeClaimTemplates is fixed — you cannot add, remove, or rename entries after creation.
  • Expanding resources.requests.storage on an existing entry is allowed (subject to StorageClass support, see Expanding storage).
  • Encryption cannot be disabled once enabled, and encryption.policyName cannot be renamed — tables already using the encrypted policy would become inaccessible.

Validation reference

Condition Result
No dataVolumeClaimSpec and no custom volume at /var/lib/clickhouse Warning — possible data loss on restart
Custom volume mounted at /var/lib/clickhouse while dataVolumeClaimSpec is set Rejected
additionalVolumeClaimTemplates set but dataVolumeClaimSpec missing Rejected
Additional disk named default Rejected — reserved by the ClickHouse default disk
Additional disk name ending in -encrypted Rejected — collides with generated encrypted disk names
Additional disk named clickhouse-storage-volume Rejected — collides with the primary data volume name
Duplicate additional disk name Rejected
Name not matching ^[a-z]([-a-z0-9]*[a-z0-9])?$ or longer than 63 characters Rejected by the CRD schema
Adding or removing dataVolumeClaimSpec after creation Rejected
Adding, removing, or renaming additionalVolumeClaimTemplates after creation Rejected
Reserved volume name in podTemplate.volumes Rejected
encryption.policyName set to default Rejected by the CRD schema — the encrypted policy must not replace the default policy
Disabling encryption or renaming its policy after creation Rejected by the CRD schema
Navigation