Skip to main content
Template version 2.0.0 is a breaking change, and one of the changes is a data-loss hazard.
  • The template no longer creates a GVC. It deploys into a GVC you already have. gvc.name is gone, and gvc.locations / gvc.pdReplicas moved to the top-level locations and pdReplicas.
  • Never upgrade a 1.x release onto 2.0.0 in place. A 1.x release owns the GVC it created, and Helm deletes what a chart stops declaring — the upgrade destroys that GVC and everything inside it. Install a new release instead: Migrating from 1.x.
  • exposeServer and devMode are removed, and a values file that still sets either one fails at render. See Access and Locations.
  • internal_access with type: workload-list never worked before 2.0.0 — a duplicate key discarded your list. It now reaches the stored spec and is enforced.

Overview

TiDB is a distributed, MySQL-compatible database designed for horizontal scalability and high availability. It separates compute from storage across three components: a SQL processing layer (TiDB Server), a distributed key-value store (TiKV), and a placement driver (PD) that manages cluster metadata and scheduling. This template deploys a TiDB cluster across one or more Control Plane locations using PingCAP’s official images, with optional scheduled backups to S3 or GCS. Database credentials are not template values. The init job and TiDB Server read the root password, application user, password and database name from a dictionary secret you create before installing, so none of them pass through Helm or land in the release.

What Gets Created

  • Stateful PD Workload — (RELEASE_NAME-pd): the placement driver quorum, pdReplicas members spread evenly across your locations. Uses replicaDirect addressing so each PD member is individually reachable.
  • Stateful TiKV Workload — (RELEASE_NAME-tikv): distributed storage nodes. Replica count per location is controlled by locations[].replicas.
  • Standard TiDB Server Workload — (RELEASE_NAME-server): MySQL-compatible SQL layer on port 4000. Per-location replica count follows locations[].replicas.
  • DB Init Cron Workload (optional, on by default) — (RELEASE_NAME-tidb-db-init): a scheduled job that sets the root password and creates the application database and user, then fast-exits on every later run.
  • Backup Cron Workload (optional) — A scheduled job that uses TiDB’s br tool to write a full cluster snapshot to AWS S3 or GCS, unsuspended in exactly one location.
  • Volume Set — PD storage (RELEASE_NAME-tidb-pd-vs): volumeset.pd.capacity GiB with no autoscaling, ext4, general-purpose SSD, with 7-day snapshot retention.
  • Volume Set — TiKV storage (RELEASE_NAME-tidb-tikv-vs): configurable capacity with optional autoscaling, ext4, general-purpose SSD, with 7-day snapshot retention.
  • Secrets — Opaque secrets containing startup scripts for PD, TiKV, TiDB Server, and the database init job. No credential secret — the passwords live only in the prerequisite secret you create.
  • Identity & Two Policies — A shared identity bound to all workloads, with reveal on the template’s own secrets plus exactly the credentials secret you created, view on the one GVC you install into so PD can confirm at boot that the GVC really has every location you listed, and cloud storage access when backup is enabled.
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.

Prerequisites

Two things must be in place before you install: a GVC with the right locations, and a credentials secret.

A GVC with your locations

A GVC must already exist, and it must contain every location you list in locations. The requirement is one-directional — the GVC may have more locations than you list, and nothing TiDB-related runs in those. Check what a GVC has before installing:
The locations are under spec.staticPlacement.locationLinks. To add a missing one:
Every workload in a GVC runs in every location that GVC has, so add locations to a shared GVC deliberately.
A location the GVC does not have is not caught at install time. The install succeeds — the platform does not validate it — and PD then refuses to bootstrap, restarting with this in the logs:
A PD member that already holds data logs a WARNING instead and keeps serving, so this check can never take down a running cluster.

Database credentials

One secret must exist before you install, when autoCreateDatabase.enabled is true (the default). It holds the credentials your applications put in their connection strings, plus the cluster’s root password. The values never pass through Helm, so they do not land in the release. Secrets are org-level, so no GVC flag is involved.
1

Create the database credentials secret

A dictionary secret holding exactly four keys — rootPassword, user, password and db. The init job sets the root password, then creates that user and that database:
Set autoCreateDatabase.credentialsSecretName to the name you used. Secret names are org-wide, so give each release its own.
2

Read the secret back later

Pass -o yaml. A bare cpln secret reveal prints only a summary table, not the values:
Create the secret before installing, or the deployment wedges silently. The template refuses to render when autoCreateDatabase.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, and every summary surface just looks like a slow deploy. The one place the reason appears is status.versions[].message:
Use get-deployments — plain cpln workload get has no versions key and will show you nothing. Creating the missing secret repairs it on its own with no further action, in roughly 5.5 to 10.5 minutes measured across several templates, or run cpln workload force-redeployment RELEASE_NAME-server --gvc GVC_NAME to clear it in about 90 seconds.
Backups need a bucket and a Control Plane cloud account before they can be enabled — see AWS S3 or GCS. Nothing else is required.

Installation

Create the prerequisite secret first, then install by whichever method you prefer:

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.8.1 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.
An in-place upgrade from 1.x to 2.0.0 destroys the cluster. Measured on a sibling template with the guard removed: the upgrade deleted the GVC and every workload, volume set and identity inside it in about six seconds, while printing upgraded successfully. Reading the GVC back afterwards returned 404. The volume sets hold your data.The chart ships a render-time refusal so this cannot happen by accident: any leftover gvc key in your values aborts the upgrade before a single API call is made, leaving your cluster untouched and running.
The guard cannot cover one case: an upgrade run with no values file at all. A 1.x release installed on pure defaults has no gvc key for the chart to see, so nothing fires and the deletion proceeds. Do not run an upgrade of a 1.x release against the 2.0.0 chart under any circumstances — install a new release and move the data across.
1

Recover the credentials the existing cluster uses

Versions up to 1.7.0 took the credentials as plain Helm values and wrote them into a chart-owned secret named after the release. Read them out of your current values file, or out of that secret:
Pass -o yaml. A bare cpln secret reveal prints only a summary table, not the values. Any password that came from a pre-1.8.0 default was published in the public template repository — treat it as compromised and choose a new one for the new cluster.
2

Back up the old cluster

Enable backup on the 1.x release, or run br backup full by hand against the old PD endpoint.
3

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.
4

Create the credentials secret and install 2.0.0 as a NEW release

Follow Database credentials, then install into that GVC with a different release name — secret names are org-wide, so a same-named release collides with the 1.x one even in another GVC.
5

Restore and cut over

Restore into the new cluster (see Restoring a Backup), then point your applications at the new RELEASE_NAME-server endpoint.
6

Uninstall the old release against the GVC you installed it into

Not the tidb-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.
Values keys that moved or were removed in 2.0.0:
  • gvc.locations is now the top-level locations, gvc.pdReplicas is now the top-level pdReplicas, and gvc.name is gone entirely.
  • devMode is gone. It only waived a three-location requirement that no longer exists — locations may hold a single location, and PD’s replication factor is now derived from the number of TiKV nodes you configure rather than mode-switched.
  • exposeServer is gone. It never published the MySQL port: opening public inbound does nothing for port 4000 without a direct load balancer the chart does not render, so the only thing the canonical endpoint would have served was TiDB’s unauthenticated status API on port 10080. Reach the server over internal GVC DNS or cpln port-forward instead.
  • replicas: 0 on a location is refused. 1.x turned it into a suspended location, and suspending a location permanently withdraws that workload’s endpoints from other locations’ service discovery. Remove the location from locations instead.
The guards fire on the mere presence of the removed keys, so exposeServer: false and devMode: false fail too. Anyone carrying a 1.x values file forward will hit an error naming the key and its replacement, rather than a silent misconfiguration.

Configuration

The default values.yaml for this template:

Locations

  • locations — List of Control Plane locations. Every one must already exist in the GVC you install into.
  • locations[].replicas — Number of TiKV and TiDB Server replicas in that location. Must be at least 1; 0 is refused at render.
  • pdReplicas — Total number of PD members across all locations, spread evenly with any remainder going to the first ones. PD is Raft-based, so it must be 1, 3, 5 or 7. A value of 1 is a single point of failure and is for testing only.
The default — one location, three TiKV nodes, three PD members — survives the loss of a node. It does not survive the loss of a location. For that, use three locations with PD spread one per location:
PD then keeps quorum when one location goes away, and TiKV spreads each region’s three copies one per location.
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.

Images and Resources

  • images.server / images.tikv / images.pd — Container images for each tier. Bump these together with backup.image: from v8.5.7, br enforces a version match with the cluster.
  • resources.pd.cpu / resources.pd.memory — CPU and memory per PD member.
  • resources.server.cpu / resources.server.memory — CPU and memory per TiDB Server replica.
  • resources.tikv.cpu / resources.tikv.memory — CPU and memory per TiKV replica.
The shipped defaults are sized for testing. For production, PD wants 4–8 CPU / 8–16Gi, TiDB Server 8–16 CPU / 16–32Gi (it scales with concurrent connections), and TiKV 8–16 CPU / 32–64Gi (memory-hungry for caching).

Database Initialization

  • autoCreateDatabase.enabled — Wires the credentials secret into the TiDB Server and init workload, and grants the identity reveal on it.
  • autoCreateDatabase.deployInitWorkload — Deploys the init job that sets the root password and creates the application database and user.
  • autoCreateDatabase.credentialsSecretName — Name of the dictionary secret holding rootPassword, user, password and db. See Database credentials.
  • autoCreateDatabase.schedule — How often the init job runs.
The init job is a cron workload, and that is deliberate. It fast-exits once the database exists — measured at 191 to 317 milliseconds of script body — so every run after the first is a no-op, and schedule really only controls how soon after install the database appears. With the default */5 * * * *, a measured install had the database created 61 seconds after helm install finished, well ahead of the five-minute worst case.
Set autoCreateDatabase.deployInitWorkload: false and upgrade if you would rather remove the job entirely once the cluster is initialized. It is cheap to leave on; the trade is a workload that runs forever for a one-time purpose.
Credentials are applied on first initialization only. Changing the secret afterwards does not change the cluster — it only changes what clients present when they authenticate. To rotate on an existing cluster, run ALTER USER inside TiDB first, then update the secret, then force a redeployment: a cpln:// reference is resolved when a replica starts and is never re-resolved while it runs.

Storage

TiKV storage (configurable):
  • volumeset.tikv.capacity — Initial volume size in GiB (minimum 10).
  • volumeset.tikv.autoscaling.enabled — Automatically expand volumes as they fill. When enabled:
    • maxCapacity — Maximum volume size in GiB.
    • minFreePercentage — Trigger a scale-up when free space drops below this percentage.
    • scalingFactor — Multiply current capacity by this factor when scaling up.
PD storage (fixed):
  • volumeset.pd.capacity — Initial volume size in GiB for PD metadata (minimum 10). PD only holds cluster metadata, so it has no autoscaling knob.
Both volume sets retain snapshots for 7 days and create a final snapshot on deletion.

Access

Internal access — configured per component (server, tikv, pd):
List only your clients. Every workload this release creates is always allowed, whatever you set — the three tiers have to reach each other, and each tier’s own replicas have to reach each other, so a workload-list naming only your applications would otherwise cut the cluster off from itself. Before 2.0.0 the workloads list never reached the stored spec at all, so workload-list blocked everything.
An access-knob change takes time to propagate — up to about ten minutes has been measured across this catalog. Keep re-polling rather than concluding the knob is broken. External access:
  • external_access.server_outboundAllowCIDR / tikv_outboundAllowCIDR / pd_outboundAllowCIDR — Outbound CIDR allowlists for each component, for reaching external services. When backup.enabled is true, TiKV outbound access is automatically set to 0.0.0.0/0 so nodes can upload directly to cloud storage, regardless of tikv_outboundAllowCIDR.
There is no public inbound access. The TiDB Server workload takes none, and exposeServer was removed in 2.0.0 because it never published the MySQL port. Reach the server over internal GVC DNS, or forward the port:
cpln port-forward is a top-level command, not a cpln workload subcommand, and it works against a workload with no public inbound at all.

Connecting to TiDB

TiDB Server is MySQL-compatible. Connect using any MySQL client from a workload in the same GVC:
Always use the fully qualified .GVC_NAME.cpln.local form. The bare workload name is not reliable on this platform — whether it resolves depends on the workload type. The pingcap/tidb image ships no MySQL client, so run the command from another workload in the same GVC — a throwaway mysql:8 workload works. Depending on how many replicas and locations you configured, the cluster can take a few minutes to accept connections.

Ports

Backup

Backup is disabled by default. When enabled, a cron workload uses TiDB’s br tool to take a full cluster snapshot on the configured schedule and upload it to AWS S3 or GCS.
  • backup.enabled — Enable scheduled backups.
  • backup.image — The backup image. Its version must match the cluster: from v8.5.7, br enforces the check even with --check-requirements=false, so bump this together with images.*.
  • backup.schedule — Cron expression for backup frequency (default: daily at 2am UTC).
  • backup.provideraws or gcp.
  • backup.location — The Control Plane location where the backup job runs. It must be one of your locations, 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 minimize transfer cost and latency.
  • backup.activeDeadlineSeconds — Maximum time allowed per backup job in seconds (default: 14400 / 4 hours).
  • backup.resources.cpu / backup.resources.memory — Resources for the backup cron container.
When backup.enabled is true, the template automatically grants TiKV outbound access to 0.0.0.0/0 so nodes can upload data directly to cloud storage. This overrides external_access.tikv_outboundAllowCIDR.

AWS S3

Before enabling backup with provider: aws, complete the following in your AWS account:
  1. Create an S3 bucket. Set backup.aws.bucket to its name and backup.aws.region to its region.
  2. If you do not have a Cloud Account set up, refer to the docs to Create a Cloud Account. Set backup.aws.cloudAccountName to its name.
  3. Create an IAM policy with the following JSON, replacing YOUR_BUCKET_NAME:
  1. Set backup.aws.policyName to the name of the policy created in step 3.
  2. Set backup.aws.prefix to the folder path where backups will be stored.

GCS

Before enabling backup with provider: gcp, complete the following in your GCP account:
  1. Create a GCS bucket. Set backup.gcp.bucket to its name.
  2. If you do not have a Cloud Account set up, refer to the docs to Create a Cloud Account. Set backup.gcp.cloudAccountName to its name.
  3. Grant the Cloud Account’s service account the Storage Admin (roles/storage.admin) role on that bucket. The chart also binds roles/storage.objectAdmin to the workload identity.
  4. Set backup.gcp.prefix to 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 land at BUCKET/PREFIX/tidb-TIMESTAMP/. Restore with br restore full, run from a workload inside the GVC*.cpln.local names do not resolve from anywhere else, and the ghcr.io/controlplane-com/backup-images/tidb-backup image is the one that carries a matching br. AWS S3:
GCS:
This restore path is the upstream procedure and has not been exercised end to end against a backup produced by this template. In particular, the SST object layout differs between the S3 (1/<name>) and GCS (1_<name>) backends. Rehearse a restore into a scratch release before you need one.
The br binary version must match your TiDB cluster version. Download it from the TiDB Community Toolkit.

Important Notes

  • Never upgrade a 1.x release onto 2.0.0 in place — it deletes the GVC the 1.x chart created and everything in it. Install a new release: Migrating from 1.x.
  • The GVC must contain every location you list, and may contain more. A missing location is not caught at install: PD exits with FATAL: locations declared in values are not in GVC .... A PD member that already holds data logs a WARNING instead and keeps serving.
  • PD’s replication factor is fixed when the cluster first bootstraps. It is the number of TiKV nodes you configure, capped at 3, and PD persists it — scaling TiKV up later does not raise it. Start with at least 3 TiKV nodes if you ever want 3-way replication.
  • There is no public access to the MySQL port. Reach the server over internal GVC DNS or with cpln port-forward.
  • Release names must be unique per org — secrets are org-wide, so two releases with the same name collide even in different GVCs.
  • Credentials apply on first initialization only. Rotate with ALTER USER inside TiDB first, then update the secret, then force a redeployment.

External References

TiDB Documentation

Official TiDB documentation

TiDB Architecture

How PD, TiKV and TiDB Server fit together

TiDB Community Toolkit

Download br and other TiDB ecosystem tools

Backup and Restore

The BR backup and restore overview

Backup Image Source

Source code for the TiDB backup container image

TiDB Template

View the source files, default values, and chart definition