How to Self-Host Ghost in 2026: MySQL, Newsletters and Content Backups
A practical Ghost self-hosting guide covering Docker, ports, persistent data, TLS, security, backups and the failures that block production use. Step by step.
A failed Ghost deployment does not always crash. It may serve a login page while the url setting is HTTP or the content volume is replaced. Start with an end-to-end check instead: complete owner setup, publish a post with an image, subscribe a member and send a test newsletter through configured email.
That check matches the cataloged purpose of Ghost: publishing platform with memberships and newsletters. It also exposes missing dependencies, wrong proxy assumptions and ephemeral data earlier than an uptime probe can.
Draw the Ghost runtime boundary
Draw three boundaries around Ghost: ingress to port 2368, durable state and supporting requirements. The container is replaceable, but the other two need explicit owners. The network contract for Ghost is MySQL 8, SMTP and optional object storage for media-heavy sites. Keep private endpoints on internal DNS, permit only required outbound calls and give Ghost a scoped service credential.
The diagram is complete when a clean client can complete owner setup, publish a post with an image, subscribe a member and send a test newsletter through configured email. Capture timing and resource data for MySQL queries, image storage, theme rendering, member count and bulk-mail provider limits. If the transaction fails, the first boundary that does not behave as documented identifies whether to investigate routing, local capacity or a supporting service.
Prove Ghost survives replacement
A container image can be downloaded again; MySQL database plus themes, images and content files cannot. Mount /var/lib/ghost/content before bootstrap, write harmless sample data and replace the container to prove that path is actually persistent. Inspect the effective mount instead of trusting a Compose filename, and check that the runtime user can write where Ghost expects.
Choose retention and an off-host destination, then rehearse recovery without touching production. The drill passes only when posts, members, newsletters, themes and images return and a test member can open the restored publication. For database-backed state, pair storage snapshots with application-consistent exports as described in point-in-time recovery versus snapshots.
Credentials, roles and exposed surfaces
The application-specific security risk is using SQLite for an unsupported production topology or leaking mail credentials. The operational answer is to protect Ghost Admin, keep mail and database credentials server-side and set the final HTTPS URL before publishing. Finish bootstrap through a restricted route and remove temporary setup access immediately afterward.
url is configuration rather than a secret; keep its value explicit while protecting the separate credentials used by Ghost. Give the Ghost process only its documented mounts and dependency routes; avoid host root and Docker socket access. Log failed authentication and configuration errors, but redact tokens, connection strings and user content.
Prove the Ghost deployment end to end
The release record for Ghost needs facts, not “looks good.” Store the selected image digest, configuration checksum, public hostname and a timestamped result for: complete owner setup, publish a post with an image, subscribe a member and send a test newsletter through configured email. Use non-production sample data so the check can run after every deployment.
Prove two lifecycle events separately. A container replacement must preserve normal operation; a clean recovery must show that posts, members, newsletters, themes and images return and a test member can open the restored publication. While the checks run, measure MySQL queries, image storage, theme rendering, member count and bulk-mail provider limits and retain the result as the expected envelope for this version.
Test a denied or invalid condition as well: temporarily deny the test identity access to MySQL 8, SMTP and optional object storage for media-heavy sites. Ghost should fail in a diagnosable way and should not overwrite healthy state. Return the valid condition, rerun the sample and attach the relevant redacted logs. Those artifacts give a future rollback decision concrete evidence.
A Docker baseline for Ghost
Keep the initial Ghost invocation reproducible enough to review in a pull request.
docker run -d \
--name ghost \
--restart unless-stopped \
-p 127.0.0.1:2368:2368 \
-v ghost-data:/var/lib/ghost/content \
-e url=https://app.example.com \
-e database__client=mysql \
-e database__connection__host=mysql.internal \
-e database__connection__user=ghost \
-e database__connection__password=replace-with-a-strong-database-password \
-e database__connection__database=ghost \
ghost:latest
Do not rely on latest after real data exists. Capture the working digest, container user and mount ownership. Follow the application log through a complete test — complete owner setup, publish a post with an image, subscribe a member and send a test newsletter through configured email — and note any migrations before putting the route behind production traffic.
TLS is easy; generated URLs are not
Set url to the final HTTPS domain before publishing. Send the chosen hostname to container port 2368, forward the original host and HTTPS scheme, and avoid publishing a second direct origin.
Test Ghost from a clean external client. Separate ingress failure from the known application boundary — the url setting is HTTP or the content volume is replaced. A certificate, DNS or 502 error belongs to routing; a request that reaches Ghost and fails later belongs to application state, capacity or its supporting requirement. The custom-domain TLS guide covers the first group.
Capacity and upgrade checks
Capacity tests should exercise MySQL queries, image storage, theme rendering, member count and bulk-mail provider limits, not a repeated request to /. Run the scenario “complete owner setup, publish a post with an image, subscribe a member and send a test newsletter through configured email” at realistic concurrency and record latency, error rate and storage growth.
Upgrade planning must account for this risk: Ghost migrations, Node runtime expectations and custom themes should be tested on a cloned site. Test the new release with representative input, then repeat the acceptance transaction and compare its result. If the url setting is HTTP or the content volume is replaced, capture the failing transaction and inspect the first boundary involved instead of assuming ingress is responsible.
Move the repeatable infrastructure work to Dockup
Routing, certificates, service replacement and attached storage are reasonable automation targets. Dockup handles those for Ghost and can provision the related managed database or connect to services on a customer's own server.
What it should not invent is the Ghost trust policy. After deployment, set url to the final HTTPS domain before publishing, enforce this boundary — protect Ghost Admin, keep mail and database credentials server-side and set the final HTTPS URL before publishing — and verify the result of this scenario: complete owner setup, publish a post with an image, subscribe a member and send a test newsletter through configured email. The outcome is one-click infrastructure with an application-specific acceptance test.
Frequently asked questions
What does Ghost need for a production deployment?
Route the Ghost container on port 2368 through one HTTPS origin. The supporting network requirement is MySQL 8, SMTP and optional object storage for media-heavy sites. Do not call Ghost ready until you can complete owner setup, publish a post with an image, subscribe a member and send a test newsletter through configured email.
Which Ghost data belongs in a backup?
Persist /var/lib/ghost/content and include MySQL database plus themes, images and content files in the same recovery manifest. A clean Ghost restore passes only when posts, members, newsletters, themes and images return and a test member can open the restored publication.
Does Ghost require HTTPS behind a reverse proxy?
Use HTTPS for the public Ghost origin and keep port 2368 on the internal route. Apply the Ghost setting correctly: set url to the final HTTPS domain before publishing. For Ghost, HTTPS protects credentials or user content in transit and keeps origin-sensitive client behavior consistent.
How should a Ghost upgrade be tested?
Restore current Ghost state into an isolated deployment, apply the candidate version and repeat its acceptance transaction. Pay particular attention because Ghost migrations, Node runtime expectations and custom themes should be tested on a cloned site. Keep the previous Ghost image until its data-migration and rollback boundary are understood.
