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
None for a default install — it deploys with a working MySQL 8 backend and an auto-assigned HTTPS endpoint. The two bundled database passwords ship aschange-me-… placeholders and must be changed at install time; see Installation.
Two optional features need setup before you install:
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 two database passwords — leave neither on itschange-me-… placeholder:
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.config.db/user/password/rootPassword— The database, user and passwords the chart writes into theRELEASE_NAME-mysql-configsecret and MySQL applies on first boot. Both passwords must be changed before installing — thechange-me-…defaults are published in the public template repository, and an install that keeps them runs with a known password. They are internal to the release: Ghost reads them from the secret, and nothing outside the GVC can reach the database.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.enablePhpMyAdmin— Off by default. Whentrue, the subchart adds aRELEASE_NAME-mysql-phpmyadminworkload that is open to the whole internet and carries the root password in its environment. Leave it off on a public site.mysql.backup.*— Scheduled dumps to S3 or GCS, off by default. See Backing Up for the bucket, Cloud Account and IAM setup.
mysql.config.* only on first startup, when the data directory is empty. Changing a password in values after the initial deployment does not change the running database — change it inside MySQL with ALTER USER and upgrade the release so the secret matches. 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 mysql.config.db 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 mysql.config.db, 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.
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 values but MySQL still uses the old one
Changed a database password in values but MySQL still uses the old one
cpln helm upgrade with a new mysql.config.password or rootPassword, Ghost fails to authenticate with Access denied for user.Cause: MySQL applies mysql.config.* only when the data directory is first initialized. The upgrade changed the secret, so 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 values and upgrade again. A clean reset is uninstall (deletes both volume sets) and reinstall.Important Notes
- Change both
mysql.configpasswords at install time — thechange-me-…defaults are public, and they seed the database on first boot; later value edits do not change the stored passwords. - 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. - Leave
mysql.enablePhpMyAdminoff on a public site — the console it adds is open to the internet and holds the root password. - 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.