Ghost is a focused, fast publishing platform — a clean alternative to WordPress for blogs, newsletters, and membership sites. The self-hosted version is a Node.js application that, for production, runs on MySQL. Getting Ghost right comes down to three things: the database, persistent storage for content, and mail.
Ghost wants MySQL 8 in production
Ghost supports SQLite for local development but officially recommends MySQL 8 for production. SQLite on a container is ephemeral and unsuitable for a real site. Provision a managed MySQL 8.x and connect Ghost to it.
Ghost reads either a config.production.json file or environment variables in the database__* form. Environment variables are the cleanest fit for a container platform:
NODE_ENV=production
database__client=mysql
database__connection__host=your-managed-mysql-host
database__connection__port=3306
database__connection__user=ghost
database__connection__password=...
database__connection__database=ghost
url=https://blog.yourdomain.comOn PandaStack, attach a managed MySQL (8.x) and map its connection details into these variables. The url must be your public HTTPS address — Ghost bakes it into links, RSS feeds, and emails, so getting it wrong breaks navigation and SEO.
Persistent storage for images
By default Ghost stores uploaded images and themes on local disk under content/. In a container — especially one that can restart or scale — local disk is ephemeral, so uploaded images vanish. You have two good options:
- 1Mount a persistent volume at
/var/lib/ghost/contentso content survives restarts. - 2Use an S3-compatible storage adapter so images live in object storage independent of the container.
For a single-instance blog, a persistent volume is the simplest correct choice. If you ever scale to multiple replicas, switch to the S3 adapter — multiple containers can't share a local volume safely.
Mail is not optional
Ghost sends two kinds of email: transactional (staff invites, password resets) via any SMTP provider, and bulk newsletter email which Ghost specifically integrates with Mailgun. If you run a membership/newsletter site, configure Mailgun:
mail__transport=SMTP
mail__options__service=Mailgun
mail__options__auth__user=...
mail__options__auth__pass=...Without working mail, member signups and magic-link logins silently fail — a frustrating bug to diagnose after launch.
Deploying
- 1Provision managed MySQL 8.x.
- 2Deploy the official
ghostimage (port 2368) as a container service with the env vars above. - 3Mount a persistent volume at the content path.
- 4Add your custom domain; SSL is issued automatically.
- 5Visit
/ghostto complete setup and create the owner account.
| Concern | Setting |
|---|---|
| Database | managed MySQL 8.x via database__* |
| Public URL | url=https://... (HTTPS) |
| Images | persistent volume or S3 adapter |
| SMTP + Mailgun for newsletters | |
| Port | 2368 |
Upgrades and backups
Ghost runs database migrations automatically on startup, so pin a version tag and upgrade deliberately. Back up two things: the MySQL database (managed MySQL gives you scheduled backups) and the content directory (snapshot the volume or rely on S3 versioning). Both are required for a full restore — the database alone won't bring back your images.
Go-live checklist
- MySQL 8.x configured, not SQLite
urlset to the public HTTPS domain- Content on a persistent volume or S3 adapter
- Mail configured (transactional + newsletter)
- Pinned Ghost version
- Database + content backups
References
- [Ghost configuration reference](https://ghost.org/docs/config/)
- [Ghost Docker image](https://hub.docker.com/_/ghost)
- [Ghost MySQL requirement](https://ghost.org/docs/install/ubuntu/)
- [Ghost storage adapters](https://ghost.org/integrations/custom-storage-adapters/)
Ghost runs as a PandaStack container service with managed MySQL attached and persistent storage for your media — plus automatic SSL on your custom domain. Launch your blog on the free tier at [dashboard.pandastack.io](https://dashboard.pandastack.io).