Skip to main content

Overview

MySQL is the most widely used open-source relational database. This template deploys a single MySQL instance on a persistent volume, with an optional phpMyAdmin web console and an optional scheduled mysqldump to an S3 or GCS bucket. Database credentials are not template values. MySQL reads the application credentials and the root password from two secrets you create before installing, so no password passes through Helm or lands in the release.
This template deploys into an existing GVC that you already have. It does not create one.

What Gets Created

The template creates no secret of its own. Every credential lives in the prerequisite secrets described below.

Prerequisites

Two secrets must exist before you install. They are deliberately separate: an application or teammate given reveal on the database credentials must not also receive root. Secrets are org-level, so no --gvc flag is involved.
1

Create the application credentials secret

A dictionary secret holding exactly three keys: username, password and database. MySQL creates this user and this database on first boot, and this is the credential your applications put in their connection strings.
Set credentialsSecretName to this name.
2

Create the root password secret

An opaque secret with encoding plain holding the MySQL root password.
Set rootPasswordSecretName to this name.
Create both secrets before installing. The template refuses to render when either name is blank, but a name that points at a secret which does not exist installs successfully and then wedges: the container never starts, cpln logs returns nothing, and the only place the missing secret is named is status.versions[].message:
Creating the missing secret repairs the deployment on its own after a few minutes, or run cpln workload force-redeployment RELEASE_NAME-mysql --gvc GVC_NAME to skip the wait.
Backups additionally need a bucket and a Control Plane Cloud Account — see Backing Up. Nothing else is required.

Installation

Install from the marketplace registry, passing the two secret names you created:
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

Image

Credentials

Both secrets must exist before installing — see Prerequisites. The workload reads them through cpln://secret/... references, so the values appear in neither the Helm release nor the stored workload spec.
MySQL applies these credentials only on first startup, when the data directory is empty. Rotating either secret afterwards does not change the stored passwords — change them inside MySQL with ALTER USER, then update the secret to match (see Troubleshooting).

Resources

Internal Access

The database is never published to the internet by this template. Reach it from inside the GVC, through cpln port-forward, or from outside through your own proxy.

Storage

phpMyAdmin

The console holds no standing credential: it has no identity, no reveal grant, and no password in its environment. It presents a login form, you supply either the application credentials or root plus the root password, and it connects to the database as whoever logged in. phpMyAdmin.internalAccess governs the console only — the database has its own internalAccess knob.
phpMyAdmin.publicAccess.enabled: true puts a database console on the public internet, where anyone who reaches it needs only a valid database password to read and write everything. Prefer leaving it off and reaching the console from inside the GVC or through cpln port-forward.

Backup

Set backup.provider to aws or gcp and fill in the matching section. The bucket, Cloud Account and permissions setup is under Backing Up.

Connecting

To reach the database from your own machine, forward the port through Control Plane, then connect with a local mysql client using the application credentials:
To verify the server from inside its own container — no password needed, the container already holds it as MYSQL_ROOT_PASSWORD:

Operations

Backing Up

With backup.enabled: true, the RELEASE_NAME-mysql-backup cron workload connects as root and runs mysqldump --databases DATABASE --single-transaction --set-gtid-purged=OFF against the database named in your credentials secret, gzips the result and uploads it as mysql-<UTC timestamp>.sql.gz under prefix in your bucket. Nothing is kept locally and nothing is ever deleted from the bucket — retention is whatever your bucket’s lifecycle rules say. The bucket and the Cloud Account must exist before you enable it.

AWS S3

  1. Create an S3 bucket. Set backup.aws.bucket to its name and backup.aws.region to its region.
  2. Create a Control Plane Cloud Account for the AWS account holding the bucket. Set backup.aws.cloudAccountName to its name.
  3. Create an IAM policy with the JSON below, replacing YOUR_BUCKET_NAME, and set backup.aws.policyName to its name. The template attaches it to the workload’s identity.

GCS

  1. Create a GCS bucket. Set backup.gcp.bucket to its name.
  2. Create a Control Plane Cloud Account for the GCP project holding the bucket, granting its service account the Storage Admin role. Set backup.gcp.cloudAccountName to its name. The template binds roles/storage.objectAdmin on exactly that bucket.

One-off dump

For an ad-hoc dump without the backup job, run mysqldump inside the database container and stream it out. The container already holds the root password and the database name as environment variables, and the stream is base64-encoded on both sides because cpln workload exec does not pass binary data through intact:
The file has the same format as the scheduled backups, so the restore below applies to both.

Restoring a Backup

The database is not reachable from the internet, so the dump is loaded from inside the database container. The mysql:9 image ships mysql, gunzip and base64, and the container holds the root password as MYSQL_ROOT_PASSWORD, so you never type it.
1

Download the dump

From a machine with access to the bucket:
2

Stream it into the running server

--stdin is required — without it the piped file is silently discarded and nothing is restored. No database name is passed because the dump was taken with --databases, so it carries its own CREATE DATABASE IF NOT EXISTS and USE statements. Each table in the dump is dropped and recreated; tables that exist on the server but not in the dump are left alone.
3

Verify

This procedure is verified against the chart’s configuration, the dump options the backup image uses and the tools present in the mysql:9 image. It has not been exercised against a live install of this template version.

Upgrading from 1.5.0

Version 1.5.1 removes the aws::ReadOnlyAccess managed policy from the backup identity. That policy granted read access to every bucket in your AWS account and contains no write actions, so it never carried the backup itself — but it was silently supplying any read action your own bucket-scoped policy happened to omit. Update your IAM policy to the full ten-action list under Backing Up before upgrading. If it already matches, no action is needed. The identity now carries cpln-connector and your bucket-scoped policy only. Nothing else changes.

Upgrading from 1.4.3 or Earlier

Template versions up to 1.4.3 took the database credentials as plain Helm values with working defaults, and deployed phpMyAdmin enabled and reachable from the entire internet. Version 1.5.0 removed both.
Carrying old values forward stops the upgrade. Both removed keys are rejected at render, so cpln helm upgrade fails and your existing release is left untouched and running rather than quietly restarting with a different password. See Troubleshooting for the exact error text.
1

Read the credentials the database already uses

Credentials are written into the data directory the first time the volume is initialized, so an existing database keeps whatever it was created with. Recover them from your current values file, or from the secret the old version created:
The keys are user, password, database and rootPassword.
2

Create the two prerequisite secrets with those same values

Follow Prerequisites, using the existing username, password, database name and root password. Different values here do not change the database — they just leave the workload unable to authenticate.
3

Remove the old keys from your values

Delete the entire config block and enablePhpMyAdmin, then set credentialsSecretName and rootPasswordSecretName to the two secret names. If you were using the console, add phpMyAdmin.enabled: true — it is off by default now, and internal-only unless you also set phpMyAdmin.publicAccess.enabled: true.
4

Upgrade, then rotate the passwords

Any password that came from a 1.4.x default was published in the public template repository — treat it as compromised. Rotate inside MySQL, then update the two secrets to match so the workload can still authenticate after a restart:
The image creates root for both localhost and %, so rotating root means changing both. Use your own username in place of appuser.

Scaling and Availability

This is a single instance on a single volume. The chart pins the workload to exactly one replica (minScale and maxScale of 1), and there is no replication or clustering mode — do not raise the replica count. Scale vertically with resources, and let the volume grow with volumeset.autoscaling. Data lives on the volume set and survives restarts, redeploys and cpln helm upgrade. cpln helm uninstall deletes the volume set and the database with it; take a backup first.

Troubleshooting

Symptom: cpln helm install succeeded, RELEASE_NAME-mysql never reaches ready, and cpln logs returns zero lines. cpln workload get RELEASE_NAME-mysql --gvc GVC_NAME shows a tag of the form cpln/secret:SECRET_NAME: secret-does-not-exist.Cause: one of the two prerequisite secrets does not exist, so the container cannot start. The missing secret is named in status.versions[].message:
Fix: create the secret exactly as in Prerequisites. The deployment recovers on its own after a few minutes, or force it:
Symptom: cpln helm upgrade stops before touching any resource with one of:
Cause: your values file still carries keys removed in 1.5.0. The failure is deliberate — it keeps the running release intact instead of restarting it with a different password.Fix: follow Upgrading from 1.4.3 or Earlier.
Symptom: after updating CREDENTIALS_SECRET_NAME or ROOT_SECRET_NAME (and redeploying), connections fail with:
Cause: MySQL reads the secrets only when the data directory is first initialized. The stored passwords are still the original ones, and the secret no longer matches them. If the root secret changed, the readiness probe (which authenticates as root) fails too and the workload stops reporting ready.Fix: put the old value back in the secret, then rotate inside MySQL and update the secret afterwards:
For root, change both 'root'@'localhost' and 'root'@'%'. To start over with a fresh database instead, cpln helm uninstall (which deletes the volume set and all data) and reinstall.
Symptom: RELEASE_NAME-mysql-backup runs on schedule and its logs end with an upload error such as:
Cause: the identity cannot write to the bucket — the IAM policy named in backup.aws.policyName is missing actions (common after upgrading from 1.5.0), or the Cloud Account does not cover the account or project holding the bucket.Fix: make the IAM policy match the ten-action list under Backing Up and confirm backup.aws.bucket / backup.gcp.bucket and the Cloud Account name are correct. The next scheduled run picks up the change.

Important Notes

  • Create both prerequisite secrets before installing. A reference to a secret that does not exist wedges the workload instead of failing the install.
  • Credentials are applied only when the volume is first initialized. Rotating a secret afterwards does not change the stored passwords — use ALTER USER inside MySQL and update the secret to match.
  • Do not scale past one replica. This is a single instance on a single volume, not a replicated cluster.
  • cpln helm uninstall deletes the volume set and the database with it. Take a backup first.
  • Access changes take a few minutes to apply after an internalAccess or publicAccess change — a connection attempt made immediately afterwards is not evidence the knob is broken.
  • Upgrading from 1.4.3 or earlier is a breaking change — the removed keys stop the upgrade rather than resetting a password. See Upgrading from 1.4.3 or Earlier.

External References

MySQL Documentation

Official MySQL reference manual

MySQL Docker Image

Official image reference, including the first-boot environment variables

phpMyAdmin Documentation

phpMyAdmin user documentation

Backup Image Source

Source code for the MySQL backup container image

MySQL Template

View the source files, default values, and chart definition