Deployment guide
Ghost, self-hosted, with the images still there.
Ghost keeps its posts in MySQL and its uploads on disk. Get the second part wrong and the site looks fine until every image disappears.
Ghost is a pleasant thing to self-host and has one sharp edge: uploaded images go to the container filesystem by default. The posts are safe in the database; the pictures are not. The next deployment takes them.
Two decisions fix it — a managed MySQL instance for the content, and a persistent volume mounted where Ghost writes its uploads.
- What it needs
- A container, managed MySQL 8, a persistent volume, and a custom domain
- Port
- 2368
- Database
- MySQL 8 — Ghost does not support PostgreSQL
- The volume
- Mounted at /var/lib/ghost/content, or the images go with the container
- The critical variable
- url — Ghost builds every link from it, including the ones in newsletters
- Typical cost
- $15 to $25 a month of metered infrastructure
How it goes
- Create MySQL 8 firstGhost supports MySQL and not PostgreSQL. Provision it and let the credentials be injected.
- Deploy the Ghost imagePoint a service at the official Ghost image and set the container port to 2368.
- Set the database variablesThe database client set to mysql, plus the connection host, user, password and database name.
- Mount a persistent volumeAt /var/lib/ghost/content. This is where themes and uploaded images live, and without it they last exactly one deployment.
- Set the url variableTo the public address, with https. Ghost generates every internal link and every newsletter link from this value.
- Attach the domainAdd the custom domain; the certificate is issued and renewed without uploading anything.
The image problem, in detail
Ghost writes uploads to /var/lib/ghost/content. Inside a container that path is ephemeral: it exists for as long as that container does, and a deployment replaces the container. The posts survive because they are in MySQL. The images do not, because they were never anywhere else.
Mount a volume there before publishing anything you would mind losing, rather than after.
Other guides in this section
- Deploy n8nDeploy n8n so workflows and credentials survive a restart: managed Postgres, a persistent volume, and the key that breaks it.
- Host a Discord BotHost a Discord bot that stays online: deployed from your repository, token stored as a secret, and nothing that sleeps.
Read next
- Persistent volumes and snapshotsPersistent volumes and snapshots on Dockup: choose mount paths, inspect usage, take and schedule snapshots, restore safely, and protect durable data.
- Custom domains and automatic TLSCustom domain and automatic TLS on Dockup: add DNS, verify ownership, issue HTTPS, expose extra ports, validate cutover, and troubleshoot safely.
- Backups you have actually restoredUntested database backups fail in predictable ways: empty dumps, missing roles, wrong flags, no encryption key. Learn how to verify a restore so the file you are keeping is one that works.
Questions this page gets asked
What does self-hosting Ghost cost?
Roughly $15 to $25 a month of metered infrastructure for the container plus a small managed MySQL instance.
Does Ghost work with PostgreSQL?
No. Ghost supports MySQL 8 and SQLite. Use managed MySQL for anything you intend to keep.
Why did my images disappear after a deploy?
They were on the container filesystem rather than a volume. Mount a persistent volume at /var/lib/ghost/content — the posts survive in MySQL, the uploads only survive on the volume.
Can I send newsletters?
Yes, with an external mail provider configured in Ghost. Make sure the url variable is the real public address first, because every link in the email is built from it.
Start on the free plan.
Ten dollars of infrastructure credit is already there, and no card is required.
