Docker image
Ghost in Docker. Pinned, hardened, MySQL 8.
Ghost is an open source publishing and newsletter platform used by blogs, magazines, and independent creators. It is a modern content management system built on Node.js, with a clean editor, native memberships and paid subscriptions, email newsletters, and a full REST and Admin API.
Prefer not to run it yourself? Managed Ghost from $5/mo
# Ghost in Docker, base pinned by digest docker pull ghcr.io/remarkablecloud/ghost:6.63.0-r2 docker compose up -d
- Current build
6.63.0-r2, updated Sep 10, 2026- Base
- Ghost 6 (Alpine), digest-pinned
- What's inside
- Env-driven config, MySQL 8 pairing, proxy-safe healthcheck, one content volume
- Upstream license
- Ghost: MIT
The image
What our image adds.
What changes from upstream Ghost, and why it matters in production.
Digest-pinned base
The image is built from ghost:alpine@sha256:04a47602872e… (Ghost 6.63.0, Node 22, Alpine). Pinning by digest keeps rebuilds reproducible: the base changes only when we bump the digest on purpose, never by accident.
Reverse-proxy-aware healthcheck
A container HEALTHCHECK probes the Ghost admin-site API and sends X-Forwarded-Proto: https, so the check passes with a 200 instead of following Ghost's HTTP to HTTPS redirect. Your platform can hold traffic until Ghost is genuinely ready.
Env-driven configuration
The url and every database__* setting come from environment variables, so one image runs unchanged from a laptop to production. Nothing environment-specific is baked in.
Immutable tags and a published changelog
Tags follow <version>-r<build> and never move, and every build is recorded in the changelog that also feeds security notifications. The current published tag is ghcr.io/remarkablecloud/ghost:6.63.0-r2.
Run it yourself, or not
Same image either way.
Pull it and run it anywhere, or let us run Ghost for you on a managed server with updates, monitoring and snapshots.
Run Ghost yourself
- Pull it from GHCR and run it on any Docker host
- The guide below: compose, environment, backups, upgrades
- You patch, back up and watch it
Ghost on a managed server
- Installed for you from this same image, $0 for the app
- Updates with a snapshot first, monitoring and a managed firewall
- A 1 GB server from $5/mo billed annually, with room for more apps
The guide
Run it in production.
Compose file, environment reference, hardening, backups and upgrades, written by the people who build the image.
On this page
Architecture at a glance
- Ghost serves plain HTTP on port
2368. TLS is terminated upstream by a reverse proxy (Traefik on the RemarkableCloud App Platform), so there is no in-container certificate to manage. - MySQL 8 is a separate database container. Ghost 6 requires MySQL 8, so SQLite and MariaDB are not supported for production use.
- A content volume at
/var/lib/ghost/contentholds themes, images, member data files, and settings that must survive a container replacement.
Docker Compose walkthrough
The standalone stack (Ghost, MySQL 8, and two named volumes) is defined in the
image’s docker-compose.yml:
name: ghost
services:
db:
image: mysql:8
restart: unless-stopped
command: ["--mysql-native-password=ON"]
environment:
MYSQL_DATABASE: ghost
MYSQL_USER: ghost
MYSQL_PASSWORD: ${DB_PASSWORD:-change-me-db}
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-change-me-root}
volumes: [db-data:/var/lib/mysql]
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-p${DB_ROOT_PASSWORD:-change-me-root}"]
interval: 10s
timeout: 5s
retries: 15
ghost:
image: ${GHOST_IMAGE:-ghcr.io/remarkablecloud/ghost:6.63.0-r1}
restart: unless-stopped
depends_on:
db:
condition: service_healthy
ports: ["${HTTP_PORT:-8083}:2368"]
environment:
url: ${GHOST_URL:-http://localhost:8083}
NODE_ENV: production
database__client: mysql
database__connection__host: db
database__connection__port: "3306"
database__connection__user: ghost
database__connection__password: ${DB_PASSWORD:-change-me-db}
database__connection__database: ghost
volumes:
- ghost-content:/var/lib/ghost/content
volumes:
db-data:
ghost-content:
Step by step:
- The database starts first.
depends_onwithcondition: service_healthymeans Ghost waits until MySQL answersmysqladmin ping, so the first boot does not race the database. - Ghost pulls the pinned image. The
GHOST_IMAGEvariable lets you override the tag for a local trial. The App Platform does not use this compose file; it deploys the catalog tag directly (currently6.63.0-r2). The compose default shown here is standalone-only. - Ghost connects over the private network.
database__connection__host: dbreaches the MySQL service by its Compose name; no database port is published to the host. - Only the web port is exposed.
${HTTP_PORT:-8083}:2368maps the Ghost HTTP port for a local run. In production, Traefik connects to2368on the internal network and nothing is bound on the host. - State lives in volumes.
ghost-contentanddb-datapersist across restarts and upgrades.
To start it locally:
DB_PASSWORD=$(openssl rand -hex 16) docker compose up -d
Then browse to http://localhost:8083 and finish setup at
http://localhost:8083/ghost.
For production, set GHOST_URL (or url) to your real HTTPS address, point the
database__* variables at your managed MySQL 8, and put the container behind a
TLS-terminating proxy. Set url to https://your-domain so Ghost generates
correct canonical links, feed URLs, and email links.
Environment variable reference
These are the values the App Platform backend wires automatically from the
managed MySQL database ({...} placeholders are filled per install). They are
listed here for standalone and self-hosted runs.
| Variable | Required | Value / default | Purpose |
|---|---|---|---|
url | yes | https://<hostname> | Public base URL Ghost uses for links, feeds, and email. Set to your HTTPS domain in production. |
NODE_ENV | yes | production | Runs Ghost in production mode. |
database__client | yes | mysql | Database driver. Ghost 6 requires MySQL 8. |
database__connection__host | yes | <db_host> | MySQL host (the db service in Compose). |
database__connection__port | yes | 3306 | MySQL port. |
database__connection__user | yes | <db_user> | MySQL user. |
database__connection__password | yes | <db_password> | MySQL password. |
database__connection__database | yes | <db_name> | MySQL database name. |
The bundled MySQL service in the standalone compose also reads
MYSQL_DATABASE, MYSQL_USER, MYSQL_PASSWORD, and MYSQL_ROOT_PASSWORD; use
a managed database in production and you can drop the db service entirely.
Mail (for member sign-in and newsletters) is configured with Ghost’s standard
mail__* variables against your SMTP provider; it is not preset by this image.
Hardening notes
- No TLS or admin secrets in the image. TLS terminates at the proxy, and every credential arrives through environment variables, so there is no baked default password and no certificate to leak.
- Digest-pinned, minimal base. The Alpine base keeps the attack surface small, and the pinned digest makes each build auditable and reproducible.
- Healthcheck gates traffic. Because the healthcheck understands the proxy protocol header, an unhealthy or half-started container is not sent live requests.
- Restart policy.
restart: unless-stoppedrecovers the service after a crash or host reboot without masking a container that is failing its health probe. - Keep the database private. Do not publish the MySQL port to the host or the internet; let Ghost reach it over the internal network only.
- Front it with a WAF or rate limiting at the proxy for the
/ghostadmin area, and consider IP allow-listing that path if only a few authors use it.
Backups
Two things must be captured together for a restorable backup:
- The MySQL database (all posts, members, and settings):
docker compose exec db mysqldump -u root -p"$DB_ROOT_PASSWORD" ghost > ghost-db.sql - The content volume at
/var/lib/ghost/content(themes, images, and uploaded files):docker run --rm -v ghost_ghost-content:/content -v "$PWD":/backup alpine \ tar czf /backup/ghost-content.tgz -C /content .
Take both at the same time and store them off the host. On the RemarkableCloud App Platform, database and volume backups are handled by the platform’s backup schedule.
Upgrades
- Minor and patch upgrades arrive as new
-rbuilds of the same or a newer Ghost version. Pull the new tag, recreate the container, and Ghost runs any database migrations on start:docker compose pull ghost && docker compose up -d ghost - Always back up first. Migrations are one-way; keep the database dump and content archive from just before the upgrade.
- Read the changelog. Every build is listed in
CHANGELOG.mdwith the upstream version, base image digest, and any behavior or breaking changes, so you can review a change before applying it. - Major Ghost versions can carry theme or Node requirements; check the entry before moving across a major line.
Changelog
Each build is pinned by digest and recorded here, so you can see what changed before you pull a new tag.
-
6.63.0-r2Current Sep 10, 2026 First changelog entry. Covers the currently promoted build,6.63.0-r2; an earlier6.63.0-r1tag of the same upstream version exists and is superseded by this one.
Ghost and other product names are trademarks of their respective owners. RemarkableCloud is not affiliated with or endorsed by them.
FAQ
Ghost in Docker, answered.
Short answers from the people who build and run this image.
Does this image include a database?
No. Ghost 6 requires MySQL 8, which runs as a separate container or managed service. The standalone compose file includes a MySQL 8 service for convenience.
Can I use SQLite or MariaDB?
Not for production. Ghost 6 supports MySQL 8 only. The image is configured for MySQL through the database__* variables.
How does HTTPS work if the container only serves HTTP?
TLS is terminated at the reverse proxy (Traefik on the App Platform). Set url to your https:// domain so Ghost emits correct links, and the proxy forwards X-Forwarded-Proto: https.
Why does the healthcheck send an X-Forwarded-Proto header?
When url is HTTPS, Ghost 301-redirects plain HTTP requests. The header tells Ghost the original request was already HTTPS, so the internal probe gets a 200 and the platform can trust the health signal.
Where is my data stored?
Posts, members, and settings live in MySQL. Themes, images, and uploads live in the content volume at /var/lib/ghost/content. Back up both together.
Pull it, or let us run it.
The same image runs Ghost on our managed servers, updates and monitoring included.