Skip to main content

Overview

MySQL is a widely used open-source relational database management system. This template deploys a single-replica MySQL instance with persistent storage, an optional phpMyAdmin web console, and optional scheduled backups to AWS S3 or GCS. Database credentials are not template values. MySQL reads both 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.
Template version 1.5.0 is a breaking security change. The config block and enablePhpMyAdmin were removed, and an install or upgrade that still sets either one now fails at render instead of silently falling back to a default password. If you are running 1.4.3 or earlier, read Upgrading From 1.4.3 or Earlier before you touch the release.
MySQL on Control Plane operates as a single-replica deployment. Do not scale up the replica count, as this would result in multiple isolated instances rather than a replicated cluster.

What Gets Created

  • Stateful Workload — (RELEASE_NAME-mysql): a single-replica MySQL container serving TCP on port 3306, with configurable resources.
  • Volume Set — (RELEASE_NAME-mysql-vs): an ext4 volume mounted at /var/lib/mysql holding all database data, with optional autoscaling.
  • Identity & Policy — An identity bound to the database and backup workloads, and a policy granting it reveal on exactly the two credential secrets you created — nothing else. When backups are enabled, the identity also carries the Cloud Account binding the backup job uses to reach your bucket.
  • phpMyAdmin Workload (optional) — (RELEASE_NAME-mysql-phpmyadmin): a serverless browser console on port 80, created when phpMyAdmin.enabled: true. It has no identity and no access to any secret.
  • Backup Cron Workload (optional) — (RELEASE_NAME-mysql-backup): a scheduled mysqldump to an S3 or GCS bucket, created when backup.enabled: true.
The template creates no credential secret of its own. Every password lives in the prerequisite secrets described below.
This template does not create a GVC. You must deploy it into an existing GVC.

Upgrading From 1.4.3 or Earlier

Template versions up to 1.4.3 took the database credentials as plain Helm values and shipped working defaults for them, and they deployed phpMyAdmin enabled and reachable from the entire internet, with no knob to restrict it short of disabling the console. Version 1.5.0 removes 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:
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. Using different values here does not change the database — it just leaves 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

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

Prerequisites

Two secrets must exist before you install. They are deliberately separate: an application handed reveal on the database credentials must not also get root. Neither value passes through Helm values, so neither lands in the release. 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 the name you used.
2

Create the root password secret

An opaque secret with encoding plain holding the MySQL root password:
Set rootPasswordSecretName to the name you used.
3

Read either secret back later

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 workload waits on the missing secret and looks broken. Confirm the database actually started with cpln workload get-deployments RELEASE_NAME-mysql --gvc GVC_NAME rather than trusting the Helm output.
Backups need a bucket and a Control Plane Cloud Account before they can be enabled — see Backup Prerequisites. Nothing else is required.

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

Configuration

The default values.yaml for this template:

Credentials

  • credentialsSecretName — Name of the dictionary secret holding username, password and database. MySQL creates that user and that database on first boot.
  • rootPasswordSecretName — Name of the opaque secret holding the root password.
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.
These credentials are only applied 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 and update the secret to match.

Resources

  • resources.minCpu / resources.minMemory — Minimum CPU and memory guaranteed to the workload.
  • resources.maxCpu / resources.maxMemory — Maximum CPU and memory the workload can use.
  • timeoutSeconds — Workload timeout in seconds.

Storage

  • volumeset.capacity — Initial volume size in GiB (minimum 10).
  • volumeset.autoscaling.enabled — Automatically expand the volume as it fills. When enabled:
    • maxCapacity — Maximum volume size in GiB.
    • minFreePercentage — Trigger a scale-up when free space drops below this percentage.
    • scalingFactor — Multiply the current capacity by this factor when scaling up.

Internal Access

  • internalAccess.type — Controls which workloads can connect to MySQL on port 3306:
The database is never published to the internet by this template. Reach it from inside the GVC, or from outside through your own proxy.

phpMyAdmin

  • phpMyAdmin.enabled — When true, deploys a phpMyAdmin workload for browser-based database management. Off by default.
  • phpMyAdmin.image — Pinned to phpmyadmin:5.2.3-apache from the official Docker library repository.
  • phpMyAdmin.publicAccess.enabled — When true, publishes the console, and its login form, to the whole internet. Off by default.
  • phpMyAdmin.internalAccess — Which workloads may reach the console, using the same four types as the database. This governs the console only; the database has its own internalAccess knob.
  • phpMyAdmin.resources.cpu / phpMyAdmin.resources.memory — Console resources.
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. Compromising the console container therefore yields no password.
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.
Access changes take up to a couple of minutes to propagate. Enabling public access was measured moving through 421 and 503 before serving 200, settling in about 34 seconds — a request made immediately after the upgrade is not evidence the knob is broken.

Backup

Set backup.enabled: true to enable scheduled database dumps to object storage. The job authenticates as root and dumps the database named in your credentials secret; both come from the same two secrets the database uses. Set backup.provider to aws or gcp and fill in the corresponding section. The prefix field controls the folder path within the bucket where backups are stored. See Backup Prerequisites for the bucket, Cloud Account and permissions setup.

Backup Prerequisites

Only needed when backup.enabled: true.

AWS S3

  1. Create an S3 bucket. Set backup.aws.bucket and backup.aws.region in your values file.
  2. If you do not have a Control Plane Cloud Account set up, follow the Create a Cloud Account guide. Set backup.aws.cloudAccountName to the name of your Cloud Account.
  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. The template attaches it to the workload’s identity.

GCS

  1. Create a GCS bucket. Set backup.gcp.bucket in your values file.
  2. If you do not have a Control Plane Cloud Account set up, follow the Create a Cloud Account guide. Set backup.gcp.cloudAccountName to the name of your Cloud Account.
  3. Add the Storage Admin role to the GCP service account associated with the Cloud Account. The template binds roles/storage.objectAdmin on exactly that bucket.

Restoring a Backup

Run the following from a client with access to the bucket, using the root password from your rootPasswordSecretName secret. Dumps carry a SET @@GLOBAL.GTID_PURGED statement that must be stripped when loading into a running server: AWS S3
GCS

Connecting

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 read 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.
  • Data lives on the volume set and survives redeploys; cpln helm uninstall deletes it, taking the database with it.
  • 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 documentation

phpMyAdmin Documentation

phpMyAdmin user documentation

Cloud Accounts

Create a Control Plane Cloud Account for backup storage access

Backup Image Source

Source code for the MySQL backup container image

MySQL Template

View the source files, default values, and chart definition