Overview
CockroachDB is a distributed SQL database that provides automatic replication, horizontal scalability, and built-in fault tolerance. This template deploys a CockroachDB cluster on Control Plane as a stateful workload with replica-direct load balancing, a PgBouncer connection pooler in front of it, and optional scheduled backups to S3 or GCS. Each location runs a configurable number of nodes that discover and join one another using Control Plane’s internal DNS. 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 survival goal toSURVIVE REGION FAILURE.
What Gets Created
- Stateful CockroachDB Workload — CockroachDB (
v25.4.0) with per-location replica counts and replica-direct load balancing. Each node drains gracefully on shutdown. - Standard PgBouncer Workload (enabled by default) — A connection pooler that multiplexes application connections across every CockroachDB node in every location. When enabled, this becomes the primary connection endpoint.
- Volume Set — Persistent ext4 storage (general-purpose SSD), one volume per node, with a final snapshot on deletion and 7-day retention.
- Identity & Two Policies — An identity bound to the workloads, with
revealon this release’s secrets,viewon the one GVC you install into so each node can confirm at boot that the GVC really has every location you listed, and cloud storage access when backup is enabled. - Secrets — The CockroachDB startup script, the PgBouncer startup script, and a small database-configuration secret.
- Backup Cron Workload (optional) — A scheduled job that issues a CockroachDB
BACKUP INTOstatement, unsuspended in exactly one location.
This template does not create a GVC. It deploys into a GVC you already have — every resource lands in the GVC you install into, so
cpln workload exec, cpln logs and uninstalling all work against that GVC, and uninstalling can never delete it. Every location you list in locations must already be on that GVC, and a GVC location you did not list simply runs nothing.Architecture
CockroachDB uses the Raft consensus protocol to replicate data across nodes. Each Control Plane location maps to a CockroachDB locality region, and nodes advertise their address via internal DNS (replica-N.WORKLOAD.LOCATION.GVC.cpln.local).
With three or more locations and the SURVIVE REGION FAILURE survival goal, the cluster tolerates the complete loss of one location without interrupting reads or writes.
Prerequisites
A GVC must already exist, and it must contain every location you list inlocations. The requirement is one-directional — the GVC may have more locations than you list, and nothing CockroachDB-related runs in those. Check what a GVC has before installing:
spec.staticPlacement.locationLinks. To add a missing one:
Installation
To install, 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
Migrating from 1.x
Template versions through 1.5.0 created their own GVC, so that GVC is part of the 1.x release’s manifest. Version 2.0.0 does not declare it — and Helm deletes what a chart stops declaring.1
Back up the old cluster
Enable
backup on the 1.x release, or issue a BACKUP INTO by hand against a node. See Backup for the bucket and cloud account setup.2
Choose the GVC for the new release
Create or pick a GVC and make sure it has exactly the locations you intend to list in
locations. See Prerequisites.3
Install 2.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 1.x one even in another GVC.
4
Restore and cut over
Restore into the new cluster (see Restoring a Backup), then point your applications at the new PgBouncer endpoint.
5
Uninstall the old release against the GVC you installed it into
Not the GVC it created — the GVC you passed at install time is where Helm tracks the release, and uninstalling from there takes the created GVC with it.
gvc.locationsis now the top-levellocations, andgvc.nameis gone entirely.replicas: 0on a location is refused at render. It used to suspend that location silently while still counting it as a region the database had been told about, which no node ever joined. Remove the location fromlocationsinstead.
Configuration
The defaultvalues.yaml for this template:
Locations and Replicas
Each entry inlocations pairs a location with a node count. Every location listed must already exist in the GVC you install into; extra GVC locations run nothing. Listing the same location twice is refused at render, as is an empty list, an entry with no name, and replicas: 0.
The default is one location with three nodes. That survives the loss of a node; it does not survive the loss of a location — see Multi-Region Survivability.
GVC locations you did not list show as red in the console, with
This workload location is deactivated because maxScale is set to 0. That is the mechanism that keeps a shared GVC safe — it is what a healthy install looks like, not a fault.Multi-Zone Placement
SetmultiZone: true to spread nodes across availability zones within each location.
Database Initialization
Thedatabase section specifies a database and user to create automatically when the cluster first initializes:
ALL on that database.
These values are only applied on the first initialization. If the cluster has already been initialized, they are skipped on restart or upgrade. To change the user or the database name on an existing cluster, use CockroachDB’s native commands (
ALTER USER, RENAME DATABASE).In CockroachDB v25, ALL on a database does not confer SELECT on tables owned by another user, so the application user cannot read tables root created. Tables the application user creates itself work normally.Resources and Storage
resources.cpuandresources.memoryset the CPU and memory allocated to each CockroachDB node.volumeset.capacitysets the initial persistent volume size in GiB (minimum 10).volumeset.autoscaling.enabled— Enable automatic volume expansion as data grows.volumeset.autoscaling.maxCapacity— Maximum volume size in GiB.volumeset.autoscaling.minFreePercentage— Triggers a scale-up when free space falls below this percentage.volumeset.autoscaling.scalingFactor— Multiplier applied to the current capacity on each scale-up.
Internal Access
Theinternal_access section controls which workloads can reach CockroachDB directly, and pgbouncer.internal_access controls who can connect through the pooler. Neither workload is exposed publicly, and the template has no public access option.
When using
workload-list, specify each workload using its full link format:
List only your clients. CockroachDB is peer-to-peer — node-to-node Raft traffic is inbound traffic to the same workload — so the chart adds this release’s own workloads (the cluster, PgBouncer and the backup cron) to every list it renders. Without that, a
workload-list naming only your applications would blackhole replication while every replica still reported ready: true.With PgBouncer enabled, the CockroachDB workload’s own list is the release’s workloads only, and internal_access.workloads is not applied to it — clients are expected to go through the pooler. Set pgbouncer.internal_access to control who reaches the pooler, and disable PgBouncer if you want clients on port 26257 directly.PgBouncer Connection Pooling
PgBouncer is enabled by default. It multiplexes application connections into a smaller pool of real database connections, reducing overhead and protecting CockroachDB from connection exhaustion under high concurrency. It is configured with every CockroachDB node across every location as a backend, so failover and load distribution are handled transparently. From 2.0.0 that backend list is built by PgBouncer’s own startup script from the same topology the CockroachDB nodes use to build their--join list, rather than being rendered separately by Helm — so the two tiers cannot disagree about which nodes exist.
When enabled, PgBouncer becomes the primary connection endpoint — connect to it instead of CockroachDB directly:
pgbouncer.defaultPoolSize— Real CockroachDB connections PgBouncer maintains per pod.pgbouncer.maxClientConn— Maximum client connections PgBouncer accepts per pod. A client beyond the limit is rejected withFATAL: no more connections allowed (max_client_conn).pgbouncer.maxDbConnections— Hard cap on total CockroachDB connections shared across all PgBouncer pods. Set this to a value your cluster can safely handle regardless of how many pods are running.pgbouncer.minReplicas/pgbouncer.maxReplicas— PgBouncer autoscales on RPS between these bounds in each configured location.pgbouncer.serverCheckDelay,serverConnectTimeout,serverLoginRetry,clientLoginTimeout,queryWaitTimeout— PgBouncer’s own timeouts, in seconds.pgbouncer.resources—minCpu/maxCpu/minMemory/maxMemoryper PgBouncer pod. These were namedcpuandmemorybefore 2.0.0; the old names are refused at render with a message naming the replacement.pgbouncer.internal_access— Controls which workloads can connect to PgBouncer (same options asinternal_access).
Connecting to CockroachDB
The cluster runs in--insecure mode, so there are no SQL credentials — access is governed by internal_access and the GVC boundary and nothing else. Control Plane provides mTLS for all inter-workload communication.
From another workload in the same GVC:
.GVC_NAME.cpln.local form. The bare workload name is not reliable on this platform — whether it resolves depends on the workload type.
The DB Console shows cluster health, query performance and node status. It is not exposed externally; forward port 8080 of the CockroachDB workload and open http://localhost:8080:
cpln port-forward is a top-level command, not a cpln workload subcommand.
Application Retry Logic
Your application must implement retry logic on database connections. PgBouncer routes around failed CockroachDB nodes, but transient errors are still surfaced to the application during failover events — such as a location outage or rolling restarts — while PgBouncer cycles through backends and Raft leader elections complete. Without retries, these transient errors will propagate directly to the client.Multi-Region Survivability
Surviving the loss of an entire location requires at least three locations, and the template’s default is one. With fewer than three, that step is skipped entirely — no CockroachDB configuration can make a one- or two-location cluster survive the loss of a region. With three or more locations, the first deploy registers every configured location as a CockroachDB region and sets the database’s survival goal toREGION. Verify it on the cluster itself:
Earlier versions could fail this step silently. The
SURVIVE REGION FAILURE statement ran in a way that swallowed its own error, so the install reported success while the database was left at the default zone survival goal. From 2.0.0 a failure is reported explicitly and prints SHOW REGIONS FROM CLUSTER. On a three-location cluster the goal is applied and verified. If you are on 1.x, check the survival goal directly rather than trusting the log’s silence.Backup
Backup is disabled by default. When enabled, a cron workload issues a CockroachDBBACKUP INTO statement on the configured schedule. CockroachDB nodes stream the backup data to cloud storage themselves using the workload identity — the backup job only triggers the SQL command and does not transfer data itself.
backup.enabled— Enable scheduled backups.backup.schedule— Cron expression for backup frequency (default: daily at 2am UTC).backup.provider—awsorgcp. Any other value is refused at render.backup.location— The Control Plane location where the backup cron runs. It must be one of yourlocations, and the chart refuses to render otherwise: the cron is suspended everywhere else, so a mismatch would mean the backup never ran anywhere with no failed run to observe. Set it to the location nearest your bucket to avoid cross-region egress.backup.activeDeadlineSeconds— Hard timeout for the backup job in seconds (default:14400/ 4 hours). The job is killed if it exceeds this limit.backup.resources.cpu/backup.resources.memory— Resources for the backup cron container.
AWS S3
Before enabling backup withprovider: aws, complete the following in your AWS account:
- Create an S3 bucket. Set
backup.aws.bucketto its name andbackup.aws.regionto its region. - If you do not have a Cloud Account set up, refer to the docs to Create a Cloud Account. Set
backup.aws.cloudAccountNameto its name. - Create an IAM policy with the following JSON, replacing
YOUR_BUCKET_NAME:
- Set
backup.aws.policyNameto the name of the policy created in step 3. - Set
backup.aws.prefixto the folder path where backups will be stored.
GCS
Before enabling backup withprovider: gcp, complete the following in your GCP account:
- Create a GCS bucket. Set
backup.gcp.bucketto its name. - If you do not have a Cloud Account set up, refer to the docs to Create a Cloud Account. Set
backup.gcp.cloudAccountNameto its name. - Add the Storage Admin role to the GCP service account associated with the Cloud Account.
- Set
backup.gcp.prefixto the folder path where backups will be stored.
Switch providers with a fresh install, not an upgrade. An identity’s cloud binding is never removed once set — the API merges rather than replaces — so a release switched from
aws to gcp keeps the old provider’s binding attached even though the chart no longer renders it.Restoring a Backup
Backups are written as a full-cluster backup collection atBUCKET/PREFIX/. Run the restore from a workload inside the GVC, or through a forwarded port to a CockroachDB node — the cluster is not reachable from outside.
Which statement you use depends on what you are restoring into, and getting this wrong is the common failure.
Into the cluster you already have
Restore the database under a new name. A full-cluster restore cannot be used here: CockroachDB refuses one on any cluster that already has user databases, and this template always createsmydb and myuser on first deploy.
'gs://BUCKET_NAME/PREFIX?AUTH=implicit'. Drop WITH new_db_name only if you have already dropped mydb.
Into an empty cluster
A full-cluster restore also brings back users and cluster settings, but 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.
External References
CockroachDB Documentation
Official CockroachDB documentation
Multi-Region Overview
Learn about multi-region deployments
Survival Goals
Configure region failure survival goals
RESTORE
The RESTORE statement reference
Backup Image Source
Source code for the CockroachDB backup container image
CockroachDB Template
View the source files, default values, and chart definition