Overview
Ghost is the open-source publishing platform for professional blogs, newsletters, and paid memberships, with a first-class editor and REST Content/Admin APIs. This template deploys a single stateful Ghost workload backed by a bundled MySQL 8 database, with a durable content volume, an HTTPS public site on the auto-assigned canonical endpoint, optional SMTP email, and optional scheduled database backups to object storage.What Gets Created
Prerequisites
Two database secrets must exist before you install; the chart never creates them. They hold credentials, so they are prerequisite secrets rather than values — a value would sit in plaintext in the Helm release for the life of the install. Secrets are org-level, so no GVC flag is involved.Database credentials
username, password and database. MySQL is initialized with it on first boot, and Ghost connects with the same keys:mysql.credentialsSecretName to this name.MySQL root password
mysql.rootPasswordSecretName to this name. The two names must differ.SMTP credentials (optional)
mail.secretName names a dictionary secret holding exactly the keys user and password. Secrets are org-level, so no GVC flag is involved:mail.secretName to this name, together with mail.host and mail.from — see Mail (SMTP).Backup bucket and Cloud Account (optional)
Installation
A default install needs only the names of the two database secrets:UI
CLI
Terraform
Pulumi
Configuration
Ghost Application
maxCpu / maxMemory — the bare cpu / memory names from template version 1.0.0 are refused at render (see Upgrading from 1.0.0). Media, themes and logs live on the content volume set; posts, members and settings live in MySQL.
Site URL
url into every page link, feed and email, and rejects admin requests whose origin does not match it, so it must be the address visitors actually use. The template sets it at container start through the RELEASE_NAME-ghost-start script, in this order:
RELEASE_NAME-ghost workload, set publicUrl to it (for example https://blog.example.com) and upgrade the release. The new value lands in the startup-script secret, not in the workload spec, so the running container keeps the old URL until it restarts — force a redeployment after the upgrade:
Mail (SMTP)
mail.secretName— Name of the prerequisite dictionary secret (keysuser,password) created in Prerequisites. Empty keeps email fully off; set, it turns on Ghost’s SMTP transport and grants the Ghost identityrevealon exactly this secret.mail.hostandmail.fromare then required — the chart refuses to render without them.mail.port/mail.secure—587withsecure: falsefor STARTTLS (default), or465withsecure: truefor implicit TLS.mail.from— The From address for outgoing mail, for example"Ghost <noreply@example.com>".staffDeviceVerification— Ghost 6 emails staff a 6-digit code on every admin sign-in from a new device. It defaults tofalsehere so/ghoststays reachable on a mail-less install; the chart refusestruewhilemail.secretNameis empty. Set it totrueonce SMTP works.
RELEASE_NAME-ghost so Ghost picks up the new credentials.
Access
publicAccess.enabled— Serve the site and the/ghostadmin over HTTPS on the auto-assigned*.cpln.appcanonical endpoint (default). Set tofalsefor an internal-only instance: Ghost then runs with the internalcpln.localURL from the Site URL table and is reachable only perinternalAccess.internalAccess.type— Which workloads can reach Ghost on port2368over the internal network:
Backing Database
mysql.image— Pinned tomysql:8. Ghost supports only MySQL 8 — not MySQL 9 and not MariaDB. Do not change it.mysql.credentialsSecretName/mysql.rootPasswordSecretName— The names of your two prerequisite database secrets (see Prerequisites): a dictionary withusername,passwordanddatabase, and an opaque secret with the root password. MySQL applies them on first boot; Ghost reads only the first. Both must exist before installing and must be two different secrets.mysql.resources/mysql.volumeset.capacity— CPU and memory bounds and the initial data volume size in GiB (minimum 10). The reservation-to-limit ratio is tuned to the platform’s cap for stateful workloads; keep it when you raise the limits.mysql.internalAccess.type— Which workloads can reach MySQL directly on port3306, using the same four types as Ghost’sinternalAccess.mysql.backup.*— Scheduled dumps to S3 or GCS, off by default. See Backing Up for the bucket, Cloud Account and IAM setup.
ALTER USER first, then update the secret’s password key (cpln secret edit SECRET_NAME) and force a redeployment of RELEASE_NAME-mysql and RELEASE_NAME-ghost (a secret change is not picked up until the workloads redeploy). To fully reset, cpln helm uninstall RELEASE_NAME --gvc GVC_NAME (which deletes both volume sets and all data) and reinstall, or install under a new release name.Connecting
200 before any owner exists:
publicAccess.enabled: false there is no canonical endpoint; reach port 2368 from another workload in the GVC, or from your machine with cpln port-forward RELEASE_NAME-ghost 2368:2368 --gvc GVC_NAME. Driving the /ghost admin through that tunnel has not been verified for this template — Ghost’s url is then the internal cpln.local address, and its admin checks requests against it.
First Run
Ghost ships no bootstrap credentials. The first visitor to/ghost creates the owner account.
Wait for both workloads to report ready
Open the admin and create the owner
https://CANONICAL_ENDPOINT/ghost and complete the setup form — site title, your name, email and password. Do this right after install: until the owner exists the site is public with the default theme and anyone who reaches /ghost can claim it.Configure mail before inviting anyone
staffDeviceVerification: true if you want the emailed sign-in code on new devices.Operations
Backing Up
Two things hold your data: the MySQL database (posts, pages, members, settings, staff accounts) and the content volume set (uploaded images, themes). A database dump does not include media. Scheduled database dumps are off by default. Whenmysql.backup.enabled: true, the RELEASE_NAME-mysql-backup cron workload runs mysqldump --databases of the database named in the database key of your mysql.credentialsSecretName secret on mysql.backup.schedule and uploads a gzipped archive named mysql-TIMESTAMP.sql.gz under mysql.backup.*.prefix in your bucket. Pick a mysql.backup.provider and complete the storage setup before enabling it:
- AWS S3
- Google Cloud Storage
Create a bucket
mysql.backup.aws.bucket and mysql.backup.aws.region to match.Set up a Cloud Account
mysql.backup.aws.cloudAccountName to its name.Create a bucket-scoped IAM policy
YOUR_BUCKET_NAME), and set mysql.backup.aws.policyName to its name. The template attaches it to the MySQL identity:RELEASE_NAME-ghost-vs volume set keeps a final snapshot for 7 days when it is deleted. Take a snapshot before a theme change or an upgrade:
mysqldump is in the mysql:8 image. Binary output through cpln workload exec is not byte-safe, so wrap the stream in base64 on both sides; the root password is already in the container’s environment:
Restoring a Backup
Scheduled dumps are gzippedmysqldump --databases output of the database named in the database key of your mysql.credentialsSecretName secret, created with --set-gtid-purged=OFF, so the archive recreates the database itself (CREATE DATABASE and USE are embedded) and carries no GTID statement to strip. Download the archive from your bucket (aws s3 cp or gsutil cp), then stream it into the running MySQL through cpln workload exec. Stdin is off by default on exec, so pass --stdin, and base64-wrap the stream so nothing is altered in transit:
cpln volumeset snapshot restore has not been exercised for this template; check cpln volumeset snapshot restore --help) or re-upload them.
Upgrading from 1.0.0
Template version 1.1.0 renamed the Ghost resource ceiling fromcpu / memory to maxCpu / maxMemory, because the block also exposes minCpu / minMemory and the bare names left it ambiguous which number was the limit. Rename the two keys in your values before upgrading:
mysql.backup.resources) keep the bare cpu / memory names.
Upgrading from 1.1.0
Template version 1.2.0 addsstaffDeviceVerification (default false), which turns off the emailed sign-in code Ghost 6 requires on every new-device admin login. On a 1.1.0 install without SMTP that code could never be delivered and /ghost was unreachable from a new browser; after the upgrade admin login works without mail. If you have SMTP configured and want the check back, set staffDeviceVerification: true.
The first cpln helm upgrade after an install can restart the bundled MySQL even when no database value changed, so run it during a quiet period.
Upgrading from 1.2.0
Template version 1.3.0 removedmysql.config (database name, user, password and root password): the database credentials are now the two prerequisite secrets under Prerequisites, the same ones the mysql template requires on its own. An upgrade that still carries mysql.config is refused at render, before anything changes; mysql.enablePhpMyAdmin is refused the same way, so remove it too.
1.2.0 created the secret RELEASE_NAME-mysql-config itself, and this upgrade deletes it. Create the new secrets under different names, holding the values your database was initialized with (not new ones, or Ghost and MySQL lose their login). Read the current values first:
user becomes username; rootPassword gets its own secret). Use your current user and database names too, not ghost, if you changed them:
mysql.credentialsSecretName and mysql.rootPasswordSecretName set to those names, and mysql.config and mysql.enablePhpMyAdmin removed. The upgrade restarts MySQL and Ghost, so the site is briefly unavailable. The new version of a workload can wait for several minutes, with get-deployments reporting that it is not allowed to reveal the secret, while access to the new secrets propagates; the previous version keeps serving meanwhile, and it clears by itself.
Scaling and Availability
Ghost is single-replica by design and this template has noreplicas knob: Ghost holds scheduler, job-queue and cache state in-process and has no upstream support for running more than one instance against the same database. Both workloads are pinned to one replica.
Durability comes from the two volume sets and, optionally, scheduled database dumps. A restart or upgrade is a brief interruption of the one replica; put a CDN in front of a busy public site to absorb it. Raise resources.maxCpu / resources.maxMemory (and the mysql.resources counterparts, keeping the reservation ratio) to scale vertically.
Troubleshooting
Upgrade fails with resources.cpu was RENAMED to resources.maxCpu
Upgrade fails with resources.cpu was RENAMED to resources.maxCpu
cpln helm upgrade from template version 1.0.0 stops at render with ghost: resources.cpu was RENAMED to resources.maxCpu (or the resources.memory equivalent). Nothing is changed on the platform.Cause: Your values still use the 1.0.0 key names.Fix: Rename resources.cpu to resources.maxCpu and resources.memory to resources.maxMemory, then upgrade again. See Upgrading from 1.0.0.Render refused with staffDeviceVerification requires SMTP
Render refused with staffDeviceVerification requires SMTP
ghost: staffDeviceVerification requires SMTP — set mail.secretName (Ghost emails the 2FA code) or leave staffDeviceVerification: false.Cause: staffDeviceVerification: true with mail.secretName empty. Ghost would email a code it has no way to send, and admin login would never complete.Fix: Configure Mail (SMTP) first, or set staffDeviceVerification: false.Admin login asks for an emailed code that never arrives
Admin login asks for an emailed code that never arrives
/ghost from a new browser prompts for a 6-digit code and no email arrives; the browser shows a server error on submit.Cause: You are on template version 1.1.0 or earlier without SMTP, or on 1.2.0 with staffDeviceVerification: true and a mail configuration that does not deliver.Fix: Upgrade to 1.2.0 (the default turns the check off) or fix the SMTP settings and credentials in mail.*; after changing the secret’s value, run cpln workload force-redeployment RELEASE_NAME-ghost --gvc GVC_NAME.Ghost workload never becomes ready and cpln logs shows nothing
Ghost workload never becomes ready and cpln logs shows nothing
RELEASE_NAME-ghost stays not ready after install with mail.secretName set, and cpln logs returns no lines for it.Cause: The SMTP secret named in mail.secretName does not exist, so the container never starts.Fix: Read status.versions[].message in cpln workload get-deployments RELEASE_NAME-ghost --gvc GVC_NAME -o yaml — it names the missing secret. Create it as shown in Prerequisites; the workload recovers on its own, or immediately after cpln workload force-redeployment RELEASE_NAME-ghost --gvc GVC_NAME.Links and emails still point at the cpln.app address after adding a custom domain
Links and emails still point at the cpln.app address after adding a custom domain
*.cpln.app endpoint — or the admin reports access denied from url.Cause: publicUrl is empty (Ghost derived the canonical endpoint), or it was changed but the container has not restarted — the value lives in the startup-script secret, which is read only at container start.Fix: Set publicUrl to the full https:// address visitors use, upgrade the release, then run cpln workload force-redeployment RELEASE_NAME-ghost --gvc GVC_NAME.Changed a database password in the secret but MySQL still uses the old one
Changed a database password in the secret but MySQL still uses the old one
password key of your database credentials secret and redeploying, Ghost fails to authenticate with Access denied for user.Cause: MySQL applies the database secrets only when the data directory is first initialized. Ghost now sends a password the database never learned.Fix: Either change the password inside MySQL to match the new value (ALTER USER 'ghost'@'%' IDENTIFIED BY '...' via cpln workload exec RELEASE_NAME-mysql --gvc GVC_NAME --container mysql -- mysql -u root -p ...), or put the old value back in the secret and force a redeployment of RELEASE_NAME-ghost. A clean reset is uninstall (deletes both volume sets) and reinstall.MySQL never becomes ready and cpln logs shows nothing
MySQL never becomes ready and cpln logs shows nothing
RELEASE_NAME-mysql stays not ready after install, and cpln logs returns no lines for it.Cause: A secret named by mysql.credentialsSecretName or mysql.rootPasswordSecretName does not exist, so the container never starts.Fix: Read status.versions[].message in cpln workload get-deployments RELEASE_NAME-mysql --gvc GVC_NAME -o yaml — it names the missing secret. Create it as shown in Prerequisites; the workload recovers on its own, or immediately after cpln workload force-redeployment RELEASE_NAME-mysql --gvc GVC_NAME.Upgrade fails with mysql.config was removed in 1.3.0
Upgrade fails with mysql.config was removed in 1.3.0
cpln helm upgrade stops at render with ghost: mysql.config was removed in 1.3.0 (or mysql.enablePhpMyAdmin was removed in 1.3.0). Nothing is changed on the platform.Cause: Your values still carry the 1.2.0 plaintext database passwords.Fix: Create the two database secrets holding your current values and remove the old keys — see Upgrading from 1.2.0.Important Notes
- Create both database secrets before installing — a missing one wedges
RELEASE_NAME-mysqlsilently; see Prerequisites. - The database secrets seed MySQL on first boot only — editing them later does not change the stored passwords: change the password inside MySQL first, then update the secret and force a redeployment of
RELEASE_NAME-mysqlandRELEASE_NAME-ghost. - Create the owner account right after install — until someone completes the form at
/ghost, the public site has no admin and anyone who reaches it can claim ownership. - MySQL 8 only — keep
mysql.image: mysql:8; Ghost does not support MySQL 9 or MariaDB. - SMTP is a prerequisite secret, not a value — create the dictionary secret (keys
user,password) before installing withmail.secretNameset; a missing secret wedges the deployment silently. - Set
publicUrlwhen you attach a custom domain, then force a redeployment ofRELEASE_NAME-ghost— links and emails use whateverurlthe container started with. staffDeviceVerification: truerequires working SMTP — the chart refuses it otherwise, and a mis-delivering mail setup locks staff out of/ghostfrom new devices.- Single replica by design — there is no
replicasknob; expect a brief interruption on restart or upgrade, and the first upgrade after install can restart the bundled MySQL. - Data survives redeploys under the same release name — content in
RELEASE_NAME-ghost-vs, the database inRELEASE_NAME-mysql-vs.cpln helm uninstalldeletes both; a database dump does not include uploaded media.