Skip to main content

Overview

ClickHouse is a column-oriented analytical database for real-time querying and data warehousing at scale. This template deploys ClickHouse in single-node or cluster mode — the mode is derived from the locations list — with object storage (AWS S3, GCS, Azure Blob Storage, or Hetzner Object Storage) as the primary store for table data and a persistent volume for metadata and a local read cache. The database password is not a template value. ClickHouse reads it from a dictionary secret you create before installing, so it never passes through Helm or lands in the release.
This template deploys into an existing GVC that you already have. It does not create or manage a GVC. Pass the GVC with --gvc GVC_NAME at install time. Every location you list in locations must already be on that GVC, and a GVC location you did not list simply starts nothing.

Deployment Modes

The locations list is the topology, not just placement: its length selects the mode, and a location’s position in the list is that shard’s number.

What Gets Created

Each server container also mounts two scratch volumes — a local filesystem cache at /var/lib/clickhouse_cache and temporary spill space at /var/lib/clickhouse-tmp. Table data lives in your bucket in every mode; the volume set holds metadata and cache only. The template creates no credential secret: the password exists only in the secret you create.

Prerequisites

Three things must be in place before you install: a GVC with the right locations, a credentials secret, and object storage for the provider you choose. Secrets are org-level, so no --gvc flag is involved when creating them.
1

Have a GVC that contains every location you will list

The GVC must already exist and must have every location you put in locations. It may have more locations than you list — nothing ClickHouse-related runs in those. Check what a GVC has before installing; the locations are under spec.staticPlacement.locationLinks:
To add a missing location, see cpln gvc add-location. Add locations to a shared GVC deliberately — other workloads in that GVC run in every location it has.
2

Create the credentials secret

A dictionary secret holding exactly two keys, password and database. ClickHouse authenticates as its built-in default user, so there is no username key:
Set database.credentialsSecretName to the name you used. Secret names are org-wide, so give each release its own.
3

Set up object storage for one provider

Object storage is required in every mode, including single-node — there is no local-only shape. Pick one provider under Object Storage below and complete its steps, then set provider to aws, gcp, azure or hetzner. AWS is the only keyless option; the other three each need their own prerequisite dictionary secret.
4

Read a secret back later

Pass -o yaml; a bare cpln secret reveal prints only a summary table, not the values:
Create every prerequisite secret before installing, or the deployment wedges silently. The chart refuses to render when a required credentialsSecretName is blank, but a name pointing at a secret that does not exist installs “successfully” and then never starts. The container never runs, so cpln logs returns zero lines — there is nothing to log. The one place the reason appears is status.versions[].message:
Use get-deployments — plain cpln workload get has no versions key. Once the secret exists the deployment recovers on its own after several minutes, or immediately with a forced redeployment:

Object Storage

Complete the steps for exactly one provider. Only the active provider’s values are used.

AWS S3

Access comes from a Control Plane cloud account through the workload identity, so there is no key to store.
  1. Create an S3 bucket. Set aws.bucket to its name and aws.region to its region.
  2. If you do not have one yet, create a Control Plane cloud account for the AWS account holding the bucket. Set aws.cloudAccountName to its name.
  3. Create an IAM policy scoped to exactly that bucket, replacing YOUR_BUCKET_NAME, and set aws.policyName to its name:
The template attaches that policy to the workload identity; the bucket in your policy is the only storage ClickHouse can reach.

GCS

ClickHouse reaches GCS over its S3-compatible interface, which requires an interoperability HMAC key. A cloud account is not required.
  1. Create a GCS bucket. Set gcp.bucket to its name.
  2. In the Google Cloud console, open Cloud Storage → Settings → Interoperability and choose Create a key for a service account. Create a new service account (or pick one) and grant it the Storage Object Admin (roles/storage.objectAdmin) role.
  3. Store the HMAC pair in a dictionary secret holding exactly the keys accessKeyId and secretAccessKey, and set gcp.credentialsSecretName to its name:
Or do the same with the gcloud CLI:

Azure Blob Storage

ClickHouse uses Azure’s native Blob Storage SDK, authenticating with a storage account access key. A cloud account is not required.
  1. In the Azure Portal, go to Storage accounts → Create. Use Standard performance, LRS redundancy, and leave hierarchical namespace off. Set azure.storageAccount to the account name.
  2. Inside the storage account, go to Containers → + Container and create a container with the access level Private. Set azure.container to its name.
  3. Go to Security + networking → Access keys and copy either key1 or key2.
  4. Store the key in a dictionary secret holding exactly the key accountKey, and set azure.credentialsSecretName to its name:
Or do the same with the Azure CLI:

Hetzner Object Storage

Hetzner Object Storage is S3-compatible and authenticates with an access key pair. A cloud account is not required. Available regions are nbg1 (Nuremberg), hel1 (Helsinki) and fsn1 (Falkenstein).
  1. In the Hetzner Cloud console, go to Object Storage and create a bucket. Set hetzner.bucket to its name and hetzner.region to its region.
  2. Go to Security → S3 Credentials and choose Generate credentials. Save the access key and secret key immediately — the secret is not shown again.
  3. Store the pair in a dictionary secret holding exactly the keys accessKeyId and secretAccessKey, and set hetzner.credentialsSecretName to its name:

Installation

With the prerequisites in place, install into your existing GVC. The default provider is AWS, so a default install needs the credentials secret and the four aws values:
The default locations entry is aws-us-east-1 with one replica. If your GVC does not have that location, or you want a cluster, set locations (and any other provider’s values) in a values file passed with -f, as shown in the CLI guide. Or follow the instructions for your preferred method:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

Locations

Each entry pairs a location with a replica count, and the list length selects the mode — see Deployment Modes. A location’s position in the list is its shard number, and Keeper runs in the first three. Two locations, a duplicate location, an empty list and replicas: 0 are all refused at render.
GVC locations you did not list show as inactive in the console, with the deployment message This workload location is deactivated because maxScale is set to 0. That is what a healthy install looks like in a shared GVC, not a fault.

Provider and Object Storage

Set provider and fill in the matching section; the setup steps for each are under Object Storage. Only the active provider’s section is read, its storage configuration secret is the only one created, and its credentials secret (for gcp, azure and hetzner) is the only one the policy grants reveal on.
Switch providers with a fresh install, not an upgrade. A cloud account binding on the workload identity is never removed once set, so a release switched from one provider to another keeps the old provider’s binding attached even though the chart no longer renders it.

Cluster and Database

clusterName is the name used in ON CLUSTER DDL and Distributed tables in cluster modes; anything other than a bare identifier is refused at render. The database key of your credentials secret names the database created at first start (with ON CLUSTER in cluster modes), and the password key is the password every client connection uses.
Renaming clusterName on an existing install orphans its Distributed tables. They keep pointing at the old cluster name and fail with Code: 701 ... Requested cluster 'my_cluster' not found (CLUSTER_DOESNT_EXIST). Recreate them after a rename.

Volumes

The volume sets hold metadata and Keeper state, not table data — that lives in your bucket. Both take a final snapshot when deleted, kept for 7 days.

Server and Keeper

keeper is only read in cluster modes. Neither workload is exposed publicly, and the template has no public access option; internalAccess controls which workloads can reach each component:
With workload-list, list only your clients. The server and Keeper reach each other over the same internal firewall, so the chart adds this release’s own server and Keeper workloads to every list it renders. Access changes can take a few minutes to take effect.

Connecting

Always use the fully qualified .GVC_NAME.cpln.local form; the bare workload name is not reliable. From another workload in the same GVC:
To verify a fresh install without a client workload, run clickhouse-client inside the server container:
Or reach the HTTP interface from your machine through a tunnel — it works even though the workload is closed to the internet:

Tables in Cluster Modes

Use ReplicatedMergeTree plus a Distributed table. A plain MergeTree in a multi-shard cluster is single-copy and is not covered by the cluster’s availability story.
{shard} and {replica} come from each node’s <macros>, which the chart derives from the location’s position in locations and the replica’s index.

Operations

Backing Up

The template ships no backup job. Table data lives in your bucket and the volume set holds only metadata and the object-storage disk metadata that points at those objects, so a volume set snapshot on its own is not a backup of your data — merges continually replace the objects the snapshot’s metadata refers to. The volume sets do take a final snapshot when deleted, kept for 7 days, which protects against an accidental uninstall but not against data loss inside ClickHouse. For a restorable copy, use ClickHouse’s own BACKUP statement to write to a separate bucket or path, as documented in the upstream backup and restore guide:
Backing up with BACKUP ... TO S3(...) has not been exercised on this template. The statement is upstream’s documented form; run it against your own install and confirm the objects land before relying on it. In cluster modes, run it on each shard’s replica (the statement backs up the replica it runs on), or back up the Distributed table’s underlying local table per shard.

Restoring a Backup

No restore path has been verified on this template. Neither RESTORE ... FROM S3(...) nor restoring a volume set snapshot has been exercised on this chart, and a volume set snapshot alone cannot restore table data because that data is in your bucket, not on the volume. Rehearse a backup and restore on a scratch release before relying on either.
Upstream’s documented form restores into the same or a different table name:
See the upstream backup and restore guide for incremental backups and the Azure destination form.

Rotating the Password

The server startup script writes the password key of your credentials secret into ClickHouse’s user configuration every time a replica starts, so a rotation takes effect when the replicas restart with the new value. Workloads resolve a secret only at replica start, so updating the secret alone changes nothing on a running replica.
1

Update the secret

Apply the whole secret manifest with the new password. Keep the database key unchanged — it names the database created at first start, and a different value makes the next start create an additional database rather than renaming the existing one:
2

Force a redeployment of the server

In cluster modes the password is also what the replicas use to talk to each other, so plan the redeployment as a query interruption until every replica has restarted. Keeper does not use the password and needs no redeployment.
3

Update your clients

Point every application at the new password. The old one is rejected once a replica has restarted.
This procedure follows from how the chart’s startup script applies the secret; it has not been exercised end to end on this template. Confirm the new password with the verify snippet under Connecting after the redeployment completes.

Upgrading from 2.x

Template versions through 2.8.0 created their own GVC, so that GVC is part of a 2.x release’s manifest. Version 3.0.0 does not declare a GVC, and Helm deletes what a chart stops declaring.
An in-place helm upgrade from any 2.x release to 3.0.0 is not supported and destroys the deployment. The upgrade would delete the GVC the 2.x chart created and every workload, volume set and identity inside it — including your ClickHouse metadata and Keeper state — while reporting success.The chart refuses to render when your values still carry the 2.x gvc key, so an upgrade that passes your old values file stops before a single resource is touched:
The guard cannot cover an upgrade run with no values at all. A 2.x release installed on pure defaults has no gvc key for the chart to see, so nothing fires. Do not run cpln helm upgrade on a 2.x release against the 3.0.0 chart under any circumstances. The only supported path is a new release against an existing GVC, reloading the data, then removing the old release.
What changed between 2.x and 3.0.0: Any removed key still present in your values is refused at render with a message naming the replacement.
1

Recover the password the existing cluster uses

Read it from your current values file or, for a release installed with 2.5.0 or earlier, from the secret that version created:
Any password that came from a pre-2.6.0 default was published in the public template repository — treat it as compromised and choose a new one for the new deployment.
2

Choose the GVC for the new release

Create or pick a GVC and make sure it has every location you intend to list in locations. See Prerequisites.
3

Install 3.0.0 as a new release into that GVC

Use a different release name — secret names are org-wide, so a same-named release collides with the 2.x one even in another GVC. Give the new release its own bucket or container: the chart writes under a fixed path, so two releases sharing one bucket would share that path too.
4

Reload your data

Do not try to reuse the old release’s volume set. It holds metadata whose shard identity and Keeper paths belong to the old topology, and it cannot be moved between releases.
5

Cut over, then uninstall the old release

Point your applications at the new endpoint, then uninstall the old release against the GVC you originally installed it into — not the GVC it created. That is where Helm tracks the release, and uninstalling from there removes the created GVC with it.

Scaling and Availability

  • locations[].replicas adds replicas to a shard; adding locations adds shards. A location’s position in locations is its shard number and the first three host Keeper, so settle the topology before loading data — existing replicated table paths and macros are derived from that position.
  • Keeper is the availability floor. The three-member group in a multi-shard cluster tolerates the loss of one member; the single-shard cluster has one member and tolerates none. If a majority of the Keeper locations are missing from the GVC, the containers exit with a named error rather than waiting for an election that can never complete.
  • With one replica per shard, losing a shard fails every distributed query, not just the rows on that shard. Add replicas per location if partial results are not acceptable.
  • A cpln helm upgrade restarts every replica in every location at once, including the first upgrade after an install even when nothing changed. Plan each one as a query interruption.
  • A node failure reschedules a replica and reattaches the same volume set; table data in the bucket is unaffected.
  • Keep every location and the bucket in the same cloud provider and region family. Cross-region traffic to object storage is billed on every query that misses the local cache.

Troubleshooting

Symptom: the install succeeds, RELEASE_NAME-clickhouse-server never becomes ready, and cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-clickhouse-server"}' --limit 50 --since 10m returns zero lines.Cause: a credentialsSecretName names a secret that does not exist, so the container is never started. The message is only visible in status.versions[].message:
Fix: create the secret as in Prerequisites. The deployment recovers on its own within several minutes, or run cpln workload force-redeployment RELEASE_NAME-clickhouse-server --gvc GVC_NAME.
Symptom: the install succeeds, but a fresh node restarts repeatedly with this in its logs:
Cause: locations names a location the GVC does not have. The platform does not validate this at install time, so each container checks its GVC at start. A node that is already initialized logs a WARNING instead and keeps serving, so this check never takes down a running cluster.Fix: add the missing location to the GVC (see Prerequisites) or remove it from locations, then upgrade.
Symptom: in a cluster mode, the server or Keeper containers exit with:
Cause: Keeper members are replica 0 of the first three entries in locations, and fewer than a majority of those locations exist in the GVC.Fix: add the missing locations to the GVC, or remove them from locations so the first three entries are all locations the GVC has.
Symptom: cpln workload get-deployments RELEASE_NAME-clickhouse-server --gvc GVC_NAME -o yaml shows a location with no replicas and the message This workload location is deactivated because maxScale is set to 0.Cause: that GVC location is not in locations. This is expected — the template runs nothing in GVC locations you did not declare. If every location shows it, none of your locations entries matches a location on the GVC, so nothing starts and there is no container to log an error.Fix: nothing, when the location is one you did not intend to use. Otherwise compare locations against spec.staticPlacement.locationLinks from cpln gvc get GVC_NAME -o yaml and correct whichever side is wrong.
Symptom: cpln helm install or cpln helm upgrade stops before creating anything with one of:
Cause: the chart validates locations, clusterName, provider and every credentialsSecretName at render time, and refuses shapes that cannot work.Fix: follow the message — use one or three-plus locations, a bare-identifier clusterName, a supported provider, and a non-empty secret name for the active provider. Your running release is unaffected until the upgrade succeeds.
Symptom: an upgrade of a 2.x release stops at render time with the message quoted under Upgrading from 2.x.Cause: your values still carry the 2.x gvc key. The refusal is deliberate — the upgrade it blocks would delete the GVC the 2.x chart created.Fix: do not retry the upgrade with the key removed. Install 3.0.0 as a new release against an existing GVC, reload the data, and remove the old release as described in Upgrading from 2.x.
Symptom: in a cluster mode, the first server replica in the first location exits with:
Cause: the bootstrap replica creates the database ON CLUSTER, which needs Keeper. It waits a bounded time for Keeper quorum rather than looping forever.Fix: check that RELEASE_NAME-clickhouse-keeper is ready in each of the first three locations with cpln workload get-deployments RELEASE_NAME-clickhouse-keeper --gvc GVC_NAME -o yaml, and read its logs with cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-clickhouse-keeper"}' --limit 50 --since 10m. Once Keeper is up the server replica is restarted automatically and bootstraps the database.
Symptom: queries against a Distributed table fail with Code: 701 ... Requested cluster 'my_cluster' not found (CLUSTER_DOESNT_EXIST).Cause: clusterName was changed on an existing install. Distributed tables keep the cluster name they were created with.Fix: drop and recreate the Distributed tables with the new cluster name, or restore the previous clusterName.
Symptom: right after a helm upgrade that changed server.internalAccess or keeper.internalAccess, clients still get the old answer.Cause: firewall changes take a few minutes to propagate.Fix: wait and re-test before concluding the setting is broken.

Important Notes

  • Never run cpln helm upgrade on a 2.x release against 3.0.0 — it deletes the GVC the 2.x chart created and everything in it. Install a new release: Upgrading from 2.x.
  • Create the credentials secret before installing. A missing one wedges the deployment with no log output; cpln workload get-deployments is the one command that names it.
  • The GVC must contain every location you list, and may contain more. Undeclared GVC locations run nothing.
  • Two locations is not supported. Use one (single-node or single-shard) or three or more.
  • Object storage is required in every mode, including single-node. There is no local-only shape.
  • The credentials secret has no username key — ClickHouse authenticates as its built-in default user.
  • Rotating the password requires a forced redeployment of the server workload to take effect.
  • Switch object-storage providers with a fresh install, not an upgrade.
  • Renaming clusterName orphans existing Distributed tables (Code: 701). Recreate them after a rename.
  • A helm upgrade restarts every replica in every location at once. Plan it as a query interruption.
  • Uninstalling deletes the volume sets (a final snapshot is kept for 7 days) but leaves your bucket and your secrets in place.

External References

ClickHouse Documentation

Official ClickHouse documentation

ClickHouse Keeper

Raft coordination for replicated tables

Data Replication

The ReplicatedMergeTree engine family

Distributed Table Engine

Fan a query out across shards

Backup and Restore

BACKUP and RESTORE to object storage

ClickHouse Template

View the source files, default values, and chart definition