Skip to main content

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.
This template deploys into an existing GVC that you already have — it does not create one.

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 as change-me-… placeholders and must be changed at install time; see Installation. Two optional features need setup before you install:
1

SMTP credentials (optional)

Member sign-in links, staff invitations and newsletters are sent over SMTP, and SMTP is off until mail.secretName names a dictionary secret holding exactly the keys user and password. Secrets are org-level, so no GVC flag is involved:
Set mail.secretName to this name, together with mail.host and mail.from — see Mail (SMTP).
2

Backup bucket and Cloud Account (optional)

Scheduled database dumps need an S3 or GCS bucket plus a Control Plane Cloud Account. See Backing Up.
Create the SMTP secret before installing with mail.secretName set. A name that points at a secret which does not exist installs “successfully” and then wedges silently: cpln logs shows nothing, and the only place the missing secret is named is status.versions[].message in cpln workload get-deployments RELEASE_NAME-ghost --gvc GVC_NAME -o yaml. The workload recovers on its own once the secret exists, or immediately after cpln workload force-redeployment RELEASE_NAME-ghost --gvc GVC_NAME.

Installation

A default install needs only the two database passwords — leave neither on its change-me-… placeholder:
Or install using 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

Ghost Application

The resource block exposes both a floor and a ceiling, so the ceiling is named 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

Ghost bakes its 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: Once you attach a custom domain to the 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 (keys user, password) created in Prerequisites. Empty keeps email fully off; set, it turns on Ghost’s SMTP transport and grants the Ghost identity reveal on exactly this secret. mail.host and mail.from are then required — the chart refuses to render without them.
  • mail.port / mail.secure — 587 with secure: false for STARTTLS (default), or 465 with secure: true for 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 to false here so /ghost stays reachable on a mail-less install; the chart refuses true while mail.secretName is empty. Set it to true once SMTP works.
The SMTP credentials are read when the container starts. After changing the secret’s value, force a redeployment of RELEASE_NAME-ghost so Ghost picks up the new credentials.

Access

  • publicAccess.enabled — Serve the site and the /ghost admin over HTTPS on the auto-assigned *.cpln.app canonical endpoint (default). Set to false for an internal-only instance: Ghost then runs with the internal cpln.local URL from the Site URL table and is reachable only per internalAccess.
  • internalAccess.type — Which workloads can reach Ghost on port 2368 over the internal network:

Backing Database

  • mysql.image — Pinned to mysql: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 the RELEASE_NAME-mysql-config secret and MySQL applies on first boot. Both passwords must be changed before installing — the change-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 port 3306, using the same four types as Ghost’s internalAccess.
  • mysql.enablePhpMyAdmin — Off by default. When true, the subchart adds a RELEASE_NAME-mysql-phpmyadmin workload 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 applies 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

Verify the site is serving once the workload reports ready — the default theme answers 200 before any owner exists:
With 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.
1

Wait for both workloads to report ready

MySQL initializes first; Ghost runs its database migrations and starts serving. Ghost retries its database connection until MySQL is up, so early connection errors in its log during a first install are expected.
2

Open the admin and create the owner

Browse to 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.
3

Configure mail before inviting anyone

Staff invitations, member sign-in links and newsletters are delivered only by email. Configure Mail (SMTP) first, then set 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. When mysql.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:
1

Create a bucket

Create an S3 bucket. Set mysql.backup.aws.bucket and mysql.backup.aws.region to match.
2

Set up a Cloud Account

If you do not have one, create a Cloud Account for your AWS account. Set mysql.backup.aws.cloudAccountName to its name.
3

Create a bucket-scoped IAM policy

Create an IAM policy granting the required S3 actions on the bucket (replace YOUR_BUCKET_NAME), and set mysql.backup.aws.policyName to its name. The template attaches it to the MySQL identity:
Content volume. The 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:
Ad-hoc dump without a bucket. 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:
This ad-hoc command has not been run against a live install of this template — try it on a throwaway release before relying on it.

Restoring a Backup

This restore procedure was written against the chart’s configuration and the backup image’s dump format. It has not been exercised against a live install of this template — rehearse it on a throwaway release before you need it, and treat any step that errors as a stop.
Scheduled dumps are gzipped mysqldump --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:
Then restart Ghost so it drops its in-memory settings and reads the restored database:
The dump restores posts, members, settings and staff accounts. Uploaded images and themes are on the content volume set, not in the database — restore those from a volume set snapshot (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 from cpu / 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:
An upgrade that still carries the old names is refused at render and your running release is left untouched:
Blocks that expose only a limit (mysql.backup.resources) keep the bare cpu / memory names.

Upgrading from 1.1.0

Template version 1.2.0 adds staffDeviceVerification (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 no replicas 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

Symptom: 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.
Symptom: Install or upgrade fails with 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.
Symptom: Signing in at /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.
Symptom: 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.
Symptom: After 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.config passwords at install time — the change-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 with mail.secretName set; a missing secret wedges the deployment silently.
  • Set publicUrl when you attach a custom domain, then force a redeployment of RELEASE_NAME-ghost — links and emails use whatever url the container started with.
  • staffDeviceVerification: true requires working SMTP — the chart refuses it otherwise, and a mis-delivering mail setup locks staff out of /ghost from new devices.
  • Single replica by design — there is no replicas knob; expect a brief interruption on restart or upgrade, and the first upgrade after install can restart the bundled MySQL.
  • Leave mysql.enablePhpMyAdmin off 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 in RELEASE_NAME-mysql-vs. cpln helm uninstall deletes both; a database dump does not include uploaded media.

External References

Ghost Documentation

Official Ghost product documentation

Configuration Reference

The url, mail and security options this template sets

Supported Databases

Why Ghost requires MySQL 8

Device Verification and Email 2FA

The staff sign-in code that staffDeviceVerification controls

Official Docker Image

The library/ghost image on Docker Hub

Ghost Template

View the source files, default values, and chart definition