Ghost
Publishing platform for blogs, newsletters and memberships
Ghost on Cubeship
Ghost is an open-source publishing platform: a website, a blog and a newsletter in one, with paid memberships built in.
This template installs it on a Cubeship instance with the managed MySQL it needs, and a volume for everything it keeps in files.
What it creates
- ghost — Ghost, from
ghost:6.63.0, answering on the domain you choose, with a volume at/var/lib/ghost/content: uploaded images and files, themes, and the routes and redirects files. - ghost-db — a managed MySQL 8.0 database, attached to the app. Posts, members, staff and settings are in it.
It needs Cubeship 0.7.0 or newer.
What you are asked
| Input | What to give |
|---|---|
| Where the site answers | A domain you control, pointed at your instance. |
| Your SMTP server | The host your mail provider gives you, like smtp.mailgun.org. |
| Its TLS port | 465 unless your provider says otherwise. |
| The SMTP username | From your mail provider. |
| The SMTP password | From your mail provider. |
| The address Ghost sends from | An address your provider lets you send as. |
Mail is not optional. Ghost emails a code whenever a staff member signs in from a device it has not seen, and the same mail carries staff invites and password resets. Without it, the first sign-in works and the next one on a new browser does not.
Ghost connects to port 465 with TLS from the start. A provider that only
offers STARTTLS on 587: set the port to 587, then change
mail__options__secure to false on the ghost app and redeploy.
This is transactional mail only. Newsletters go out through Mailgun, configured separately in Ghost's settings.
After installing
- Open
https://<your domain>/ghoststraight away. Ghost has no default account: the first person to open it creates the owner. Until you do, that is anyone who finds the domain. - Before signing out, sign in again from a private window. Ghost emails a code to the owner's address; if it never arrives, fix the SMTP settings while you still have a session.
The SMTP settings are variables on the ghost app. Change them and redeploy.
What does not work here
- Social web (ActivityPub). Ghost expects
/.ghost/activitypub/on its domain to reach a separate service, and a domain on Cubeship sends every path to the one app. It is on by default: turn Social web off in Ghost's settings so Ghost stops trying. - Web analytics. Ghost's built-in traffic analytics need a Tinybird account and a tracking service beside Ghost. Without them Ghost leaves them off.
The volume
The app runs as one copy on the machine its volume is on, and a deploy stops it for a few seconds. Back up both the volume, for images and themes, and the database, for everything else: one without the other is a site with broken images or no posts.
Resources
The app is limited to 1 CPU and 1 GiB of memory. Raise limits in
template.yaml for a busy site.
What this creates
ghost
ghost:6.63.0
ghost-db
MySQL 8.0
/var/lib/ghost/content
Volume of ghost
# yaml-language-server: $schema=https://cubeship.dev/schema/template/v1.json
version: 1
# The first release that keeps a volume's data across deploys.
minCubeship: "0.7.0"
project: ghost
inputs:
- key: domain
type: domain
label: Where the site answers
- key: smtpHost
type: text
label: Your SMTP server
help: Ghost emails a code to sign in from a new device, so without working mail nobody can sign in twice.
- key: smtpPort
type: number
label: Its TLS port
help: Ghost connects with TLS from the first byte, which providers offer on 465.
default: 465
min: 1
max: 65535
- key: smtpUser
type: text
label: The SMTP username
- key: smtpPassword
type: secret
label: The SMTP password
- key: mailFrom
type: text
label: The address Ghost sends from
help: One your SMTP provider lets you send as, like hello@example.com.
pattern: ^[^@\s]+@[^@\s]+\.[^@\s]+$
databases:
- key: db
name: ghost-db
# Ghost supports MySQL 8 only, and tests against 8.0.
engine: mysql
version: "8.0"
database: ghost
apps:
- key: web
name: ghost
image: ghost
tag: "6.63.0"
port: 2368
# Every other route redirects a probe that did not arrive over HTTPS;
# this one is outside that check and answers 200 without signing in.
health: /ghost/.well-known/jwks.json
domains:
- host: ${input.domain}
attach:
- database: db
volumes:
- path: /var/lib/ghost/content
limits: { cpu: 1, memory: 1Gi }
env:
url: https://${input.domain}
# Ghost reads its connection piece by piece, not from DATABASE_URL.
database__client: mysql
database__connection__host: ${db.db.host}
database__connection__port: ${db.db.port}
database__connection__user: ${db.db.user}
database__connection__password: ${db.db.password}
database__connection__database: ${db.db.name}
mail__transport: SMTP
mail__options__host: ${input.smtpHost}
mail__options__port: ${input.smtpPort}
mail__options__secure: "true"
mail__options__auth__user: ${input.smtpUser}
mail__options__auth__pass: ${input.smtpPassword}
mail__from: ${input.mailFrom}