Ghost
Ghost is a publishing platform: posts, newsletters sent to subscribers, and paid memberships. Two containers — the application and its MySQL database. Fediverse federation is available as an optional overlay.
Last run on a host 8 Nov 2026 · data restored from a backup
Pinned at 6.75.0-alpine (released 4 Nov 2026) · the newest upstream release
In the upstream images: 2 CRITICAL · 45 HIGH with a published fix — per image
To try it: Docker, nothing else. See Try it locally.
To deploy it behind the proxy: Traefik running, a domain, and SMTP credentials.
Try it locally
Section titled “Try it locally”On your own machine, with no proxy, domain or certificate. From apps/ghost/:
cp .env.local.example .env.local
# The secrets ship empty; generate the ones this stack needs{ echo "DB_PASSWORD=$(openssl rand -hex 32)" echo "DB_ROOT_PASSWORD=$(openssl rand -hex 32)"} >> .env.local
mkdir -p volumes/local/content
docker compose -f docker-compose.local.yml --env-file .env.local up -dThen open http://localhost:2368. Stop it again with the same command and down.
Installation
Section titled “Installation”From apps/ghost/:
-
Copy the environment file:
Terminal window cp .env.example .envSet
APP_TRAEFIK_HOST,TZ, and the mail block —GHOST_MAIL_HOST,GHOST_MAIL_PORT,GHOST_MAIL_USER,GHOST_MAIL_FROM. Port 587 withGHOST_MAIL_SECURE=falseis the common combination; 465 needsGHOST_MAIL_SECURE=true. -
Generate the secrets, including the SMTP password:
Terminal window mkdir -p .secretsopenssl rand -base64 32 | tr -d '\n' > .secrets/db_pwd.txtopenssl rand -base64 32 | tr -d '\n' > .secrets/db_root_pwd.txtprintf '%s' 'YOUR-SMTP-PASSWORD' > .secrets/mail_pwd.txtGhost has no
_FILEform for these, so the stack’s entrypoint reads the files at start and exports the values before handing off. -
Start it:
Terminal window docker compose up -dFirst start runs database migrations and can take 30 to 60 seconds. The healthcheck allows for it; on slow storage the container may show
unhealthybriefly before recovering. -
Over the VPN, open
https://<APP_TRAEFIK_HOST>/ghost/and complete the setup wizard. Ghost mails a verification code for every login from a new device, including this one, so mail has to work before the wizard can be finished. -
Open the blog to the internet once the owner exists:
Terminal window sed -i 's/^APP_TRAEFIK_ACCESS=.*/APP_TRAEFIK_ACCESS=access-public/' .envdocker compose up -d
Verify
Section titled “Verify”docker compose ps # two services, healthydocker compose logs ghost-app --tail 50 # migrations completeddocker compose logs db --tail 20 # ready for connectionsThen register a test member on the public site and confirm the verification mail arrives. That is the check that matters: signups fail silently when mail is broken, so a working site tells you nothing about it.
Going further
Section titled “Going further”Who can reach it
Section titled “Who can reach it”The setup page answers until the owner exists, and there is no setup secret —
whoever reaches it first becomes the owner. The stack therefore ships
access-vpn, and the blog becomes public in step 5, by your decision. A public
blog then runs access-public with limit-standard and headers-standard.
Mail that arrives rather than mail that sends
Section titled “Mail that arrives rather than mail that sends”Ghost uses the Mail-From domain as written. SPF, DKIM and DMARC for the domain
in GHOST_MAIL_FROM have to exist at your DNS provider, or member mail lands in
spam while the send itself reports success.
Fediverse federation
Section titled “Fediverse federation”activitypub.yml adds two services — the federation backend and a one-shot
migration runner. Enable it with COMPOSE_FILE=docker-compose.yml:activitypub.yml
in .env.
On a deployment where MySQL already holds data, create the ActivityPub database once before enabling the overlay:
docker exec ghost-db sh /docker-entrypoint-initdb.d/01-activitypub-db.shThen docker compose up -d. Check the migration ran and exited cleanly:
docker compose logs activitypub-migratedocker compose logs ghost-activitypub --tail 30Backup
Section titled “Backup”| Database | MySQL, container ghost-db, database ghost, user ghost_user |
| Password | .secrets/db_pwd.txt |
| State | volumes/mysql (database) · volumes/content (themes, images, member data) |
| Quiescing | Not needed — the dump is consistent on its own. |
Posts reference images in volumes/content by path, so the database and the
content directory come back from the same backup run.
For borgmatic:
mysql_databases: - name: ghost container: ghost-db username: ghost_user password: "{credential file /srv/docker/apps/ghost/.secrets/db_pwd.txt}"volumes/content holds the active theme, not only uploads. A database-only
restore brings every post back and renders them with the default theme, which
looks like a broken site rather than a partial restore.
Restore order: database, then the application.
Updates
Section titled “Updates”-
Read the release notes — Ghost’s major versions carry migrations.
-
Back up the database and the content directory together.
-
Raise
APP_TAGin.env, then:Terminal window docker compose pull && docker compose up -ddocker compose logs ghost-app --follow -
Load the public site and the administration interface. A theme incompatible with the new version shows up on the front page, not in the log.
Troubleshooting
Section titled “Troubleshooting”The login code never arrives, and there is no way in. Mail is not working.
Check GHOST_MAIL_* in .env and the password in .secrets/mail_pwd.txt, then
recreate the app container. This blocks administration entirely, so treat it as
the first thing to fix.
Member signups do nothing. The same cause. The signup form reports success and the confirmation mail never leaves.
Member mail arrives in spam. SPF, DKIM and DMARC for the GHOST_MAIL_FROM
domain are missing at the DNS provider.
The container is unhealthy for the first minute. First-start migrations.
It recovers on its own; if it does not, read docker compose logs ghost-app.
Federation shows redirect errors after a configuration change. Browsers cache redirect chains. Confirm in a private window before changing anything else.
Repository files
Section titled “Repository files”apps/ghost/README.md— setup, secret handling, known issuesapps/ghost/UPSTREAM.md— source and upgrade checklistapps/ghost/activitypub.yml— the federation overlay
Official sources
Section titled “Official sources”© 2026 SecDockBlueCode Apache-2.0Site content CC BY-NC 4.0