Overview
CockroachDB is a distributed SQL database with PostgreSQL wire compatibility, automatic replication and built-in fault tolerance. This template deploys a CockroachDB cluster (cockroachdb/cockroach:v25.4.0) with a configurable number of nodes in each location you list, a PgBouncer connection pooler in front of it, and an optional cron job that writes scheduled full-cluster backups to S3 or GCS.
On first deployment the cluster initializes itself and creates a database and user. With three or more locations it also registers every location as a CockroachDB region and sets the database’s survival goal to SURVIVE REGION FAILURE, so the loss of one whole location does not interrupt reads or writes. The default is a single location with three nodes, which survives the loss of a node but not of a location.
locations must already be on that GVC, and a GVC location you did not list simply starts nothing — no CockroachDB node and no PgBouncer replica runs there.What Gets Created
internal_access and the GVC boundary. Neither workload is exposed publicly.
Prerequisites
The GVC you install into must already have every location you list inlocations. The default list is one location, aws-us-east-1; if your GVC does not have it, change locations before installing (see Locations). The requirement is one-directional — the GVC may have more locations than you list, and nothing CockroachDB-related runs in those.
Check the GVC's locations
spec.staticPlacement.locationLinks:Add a missing location, or change your values
cpln gvc add-location, documented in the gvc CLI reference) or edit locations to match what the GVC has. Every workload in a GVC runs in every location that GVC has, so add locations to a shared GVC deliberately.Installation
A default install needs no values whenGVC_NAME has the aws-us-east-1 location:
locations list in a values file and pass it with Helm’s -f values.yaml. locations is a list, so it cannot be set with --set — the chart refuses --set locations=[] with a message saying so.
UI
CLI
Terraform
Pulumi
Configuration
Each block below is the shipped default for that part ofvalues.yaml.
Locations
name, a location has replicas: 0, or the total is a single node:
replicas: 0is refused. To stop running in a location, remove it fromlocations— a zero-replica location would still be added to the database as a region that no node ever joins.- A one-node deployment is refused. Each node joins every other node, so a single node has nothing to join and cannot start. Use at least 3 replicas in total; 3 in one location is the smallest supported shape, matching CockroachDB’s default replication factor of 3.
This workload location is deactivated because maxScale is set to 0. That is what a healthy install looks like on a GVC with extra locations, not a fault.Image and Multi-Zone Placement
multiZone: true spreads the nodes across availability zones within each location.
Resources
locations[].replicas and vertically here.
Database
ALL on that database. On an existing cluster, changing these values does nothing — use ALTER USER or ALTER DATABASE ... RENAME TO inside CockroachDB instead.
Storage
ext4 volume of this size on general-purpose-ssd storage, with a final snapshot on deletion retained for 7 days.
Internal Access
internal_access governs direct connections to the CockroachDB nodes only when PgBouncer is disabled. With PgBouncer enabled (the default), the nodes accept connections from this release’s own workloads only, clients are expected to go through the pooler, and pgbouncer.internal_access is the knob that controls who reaches it.
workload-list it renders, because node-to-node replication is inbound traffic to the same workload and would otherwise be cut off. You never need to list them yourself.PgBouncer
maxDbConnectionsis a hard cap on real CockroachDB connections shared across all PgBouncer replicas — set it to what your cluster can safely accept regardless of how many replicas are running.minReplicas/maxReplicasapply per location; PgBouncer autoscales on request rate between them.resourcesnames the limitmaxCpu/maxMemorybeside the reservationminCpu/minMemory. The pre-2.0.0 namescpuandmemoryare refused at render with a message naming the replacement.- The remaining timeouts are PgBouncer’s own settings, in seconds — see the PgBouncer configuration reference.
Backup
backup.location must be one of locations: the backup cron runs in exactly that location and is suspended everywhere else, and the chart refuses to render otherwise.
Connecting
The template exposes no public endpoint. Reach the cluster from workloads in the same GVC, or from your own machine throughcpln port-forward.
--replica names one replica of the stateful workload; on a release with more than one location, add --location LOCATION to pick the location:
is_available and is_live both true. To open the DB Console — cluster health, node status and SQL activity — from your machine, forward port 8080 and open http://localhost:8080:
Operations
Backing Up
Whenbackup.enabled is true, the RELEASE_NAME-cockroach-backup cron workload runs on backup.schedule and issues one SQL statement against the cluster (through PgBouncer when it is enabled):
gs://BUCKET_NAME/PREFIX?AUTH=implicit on GCS.) Two consequences shape how you restore:
- It is a full-cluster backup, not a backup of
mydbalone: every database, every table, users and cluster settings land in one backup collection atBUCKET_NAME/PREFIX/. Each run adds a new full backup to that collection; nothing deletes older ones, so manage retention with a bucket lifecycle rule. - The CockroachDB nodes upload the data themselves, authenticating with the workload identity’s cloud account binding (
AUTH=implicit). The cron job only triggers the statement, which is why the cloud permissions are onRELEASE_NAME-cockroach-identityand the restore statements later on this page run from a node.
backup.location must be one of locations — the cron is suspended everywhere else, and the chart refuses to render otherwise, because the platform accepts a location that does not exist and the job would then never run anywhere with no failed run to observe. Set it to the location nearest your bucket to avoid cross-region egress.
AWS S3
- Create an S3 bucket. Set
backup.aws.bucketto its name andbackup.aws.regionto its region. - If you do not have a Control Plane cloud account for AWS, follow the Create a Cloud Account guide. Set
backup.aws.cloudAccountNameto its name. - Create an IAM policy with the following JSON, replacing
YOUR_BUCKET_NAME, and setbackup.aws.policyNameto its name. The template attaches this policy to the identity, so the bucket in it is the only storage the nodes can reach:
- Set
backup.aws.prefixto the folder path for the backup collection, then setbackup.enabled: true,backup.provider: awsandbackup.location.
GCS
- Create a GCS bucket. Set
backup.gcp.bucketto its name. - If you do not have a Control Plane cloud account for GCP, follow the Create a Cloud Account guide. Set
backup.gcp.cloudAccountNameto its name. - Add the Storage Admin role to the GCP service account associated with the cloud account. The template additionally binds the identity to
roles/storage.objectAdminon exactly the bucket inbackup.gcp.bucket. - Set
backup.gcp.prefixto the folder path for the backup collection, then setbackup.enabled: true,backup.provider: gcpandbackup.location.
Backup complete.:
backup.provider is gcp):
backup.provider with a fresh install, not an upgrade. An identity’s cloud binding is merged rather than replaced when the identity is updated, so a release switched from aws to gcp keeps the old provider’s binding on RELEASE_NAME-cockroach-identity even though the chart no longer renders it.Restoring a Backup
The backup is a full-cluster backup collection, and whichRESTORE statement you use depends on what you are restoring into — getting this wrong is the common failure. Both statements below run from a node, because the nodes hold the cloud credentials; the URIs are the same ones the backup wrote to (gs://BUCKET_NAME/PREFIX?AUTH=implicit on GCS).
RESTORE syntax, but they have not been rehearsed against a live install of this template version. Rehearse on a throwaway release before you depend on them.mydb and myuser on first deploy.
mydb_restored, or drop mydb and rename. Drop WITH new_db_name only if you have already dropped mydb.
Into an empty cluster — a full-cluster restore, which also brings back users and cluster settings. It requires the target to have no user databases at all, so drop the ones this template created first:
full cluster restore can only be run on a cluster with no tables or databases. Follow progress with SHOW JOBS the same way, and verify afterwards with SHOW DATABASES.
Upgrading from 1.x
Template versions through 1.5.0 created their own GVC, so that GVC is part of a 1.x release’s manifest. Version 2.0.0 does not declare a GVC — and Helm deletes what a chart stops declaring.Back up the old cluster
backup on the 1.x release, or issue a BACKUP INTO by hand against one of its nodes. See Backing Up for the bucket and cloud account setup.Choose the GVC for the new release
locations. See Prerequisites.Install 2.0.0 as a NEW release into that GVC
Restore and cut over
Uninstall the old release against the GVC you installed it into
Scaling and Availability
-
Node loss. With three nodes in a location (the default), CockroachDB’s replication factor of 3 keeps every range available through the loss of one node. Scale a location by raising
locations[].replicasand upgrading; new nodes join the cluster on their own. -
Location loss needs three or more locations. With three or more entries in
locations, the first deploy registers every location as a CockroachDB region and sets the survival goal ofdatabase.nametoSURVIVE REGION FAILURE, so the cluster tolerates the loss of an entire location. With one or two locations that step is skipped — no CockroachDB setting can make a one- or two-location cluster survive a region loss. Verify what your cluster actually has:If the region setup did not complete, the start log of the first node says so explicitly (ERROR: Multi-region configuration is INCOMPLETE) and printsSHOW REGIONS FROM CLUSTER; see Troubleshooting. - Rolling restarts during an outage are not covered. A cluster that is already missing a location is at its fault-tolerance limit; rolling out or restarting nodes in the remaining locations during that outage causes a brief period of unavailability for ranges on those nodes. Wait for the lost location to return before upgrading.
- Your application must retry. PgBouncer routes around failed nodes, but transient errors still reach the client during failover events — a location outage or a rolling restart — while PgBouncer cycles through backends and leader elections complete. Without retries those errors propagate to your users.
-
Removing a location from
locationsdoes not drain its nodes. They refuse to start rather than rejoining from a region the database was not told about. Decommission the nodes in that location first if they hold data. -
PgBouncer scales on its own between
pgbouncer.minReplicasandpgbouncer.maxReplicasin each location; raisemaxReplicaswhen the pooler, not the database, becomes the bottleneck. -
Data survives restarts and upgrades, not an uninstall:
cpln helm uninstallremoves the volume set with the release, taking a final snapshot retained for 7 days. Take a backup first if the data matters.
Troubleshooting
Nodes restart with FATAL: locations declared in values are not in GVC
Nodes restart with FATAL: locations declared in values are not in GVC
cpln logs for it shows:locations names a location the GVC does not have. The platform accepts this silently; the nodes catch it at boot and refuse to initialize rather than forming a cluster whose regions can never exist.Fix: add the missing location to the GVC, or remove it from locations and upgrade. See Prerequisites.A node exits with FATAL: this node is running in location X, which is not in the configured location list
A node exits with FATAL: this node is running in location X, which is not in the configured location list
locations with a replica count and upgrade.helm install or upgrade fails with a cockroach: render error
helm install or upgrade fails with a cockroach: render error
cpln helm install or cpln helm upgrade exits before anything is applied with an execution error at (cockroach/templates/identity.yaml ...) message, and the existing release is untouched.Cause: the chart validates the values at render time. The message names the problem exactly — a leftover 1.x gvc key, an empty or duplicated locations entry, replicas: 0, a single node in total, a backup.location that is not one of locations, a backup.provider other than aws or gcp, a missing backup field, or the renamed pgbouncer.resources.cpu / memory.Fix: change the values as the message says and run the command again. For the gvc key, do not work around it — see Upgrading from 1.x.The CockroachDB workload never becomes ready after enabling multiZone, and cpln logs is empty
The CockroachDB workload never becomes ready after enabling multiZone, and cpln logs is empty
multiZone: true the workload never reaches ready, no container starts, and cpln logs returns no lines.Cause: a location in the GVC does not support multi-zone placement for this workload. The setting is accepted and the deployment wedges without a log line.Fix: read status.versions[].message from cpln workload get-deployments RELEASE_NAME-cockroach --gvc GVC_NAME -o yaml, set multiZone: false and upgrade.ERROR: Multi-region configuration is INCOMPLETE in the start log
ERROR: Multi-region configuration is INCOMPLETE in the start log
ERROR: Multi-region configuration is INCOMPLETE. The database is serving, but it cannot survive the loss of a region. followed by the output of SHOW REGIONS FROM CLUSTER.Cause: one or more locations in locations had no nodes running when the regions were added — typically because the location is missing from the GVC, or its nodes had not joined yet.Fix: confirm every location in locations exists in the GVC and has nodes listed by cockroach node status, then run the ALTER DATABASE mydb ADD REGION "LOCATION" and ALTER DATABASE mydb SURVIVE REGION FAILURE statements by hand from a node, and verify with SHOW SURVIVAL GOAL FROM DATABASE mydb.full cluster restore can only be run on a cluster with no tables or databases
full cluster restore can only be run on a cluster with no tables or databases
RESTORE FROM LATEST IN ... fails with that error.Cause: the backup is a full-cluster backup, and CockroachDB refuses to restore one onto a cluster that already has user databases — which this template always creates on first deploy.Fix: either restore the database under a new name with RESTORE DATABASE mydb ... WITH new_db_name, or drop mydb and myuser first. Both paths are under Restoring a Backup.Clients can still connect after tightening internal_access
Clients can still connect after tightening internal_access
internal_access or pgbouncer.internal_access, previously allowed clients keep connecting, or newly allowed ones are still refused.Cause: firewall changes take up to a few minutes to propagate.Fix: wait and re-test for several minutes before concluding the knob did not work.Important Notes
- Every location in
locationsmust already exist in the GVC. A missing one is accepted at install and refused by the nodes at boot; a GVC location you did not list runs nothing. - Never
helm upgradea 1.x release onto 2.0.0. It deletes the GVC the 1.x release created and everything in it. Install a new release and move the data — see Upgrading from 1.x. - Surviving a region loss needs at least three locations. The default single location survives a node failure, not a location failure.
- The cluster runs in insecure mode. There are no SQL credentials;
internal_access,pgbouncer.internal_accessand the GVC boundary are the only access control. - Confirm multi-zone support in every GVC location before setting
multiZone: true. An unsupported location wedges the deployment with no log output. - Use at least 3 replicas in total. A one-node deployment is refused at render;
replicas: 0on a location is refused too — remove the location instead. - Removing a location does not drain its nodes. Decommission them first if they hold data.
- Backups are full-cluster backups. Restoring into a live cluster must use
RESTORE DATABASE mydb ... WITH new_db_name, never a bareRESTORE FROM LATEST IN— see Restoring a Backup. backup.locationmust be one oflocations. The cron runs only there; anywhere else it is suspended.- Your application must retry transient errors. Failover events surface them through PgBouncer.
- Access-knob changes take up to a few minutes to apply. Re-test before concluding a change did not work.
cpln helm uninstalldeletes the volume set and with it the data, after a final snapshot retained 7 days. Back up first.