How to Deploy Ghost CMS with Docker in 15 Minutes

How to Deploy Ghost CMS with Docker in 15 Minutes

Introduction

Ghost CMS is one of the best open-source publishing platforms out there — fast, modern, and built for serious content creators. And the good news? With Docker, you can self-host a fully functional Ghost 5 instance in under 15 minutes on your own server.

In this tutorial, I'll walk you through exactly how I deployed the BOTUM blog (blog.botum.ca) using Ghost 5, Docker Compose, Zoraxy as an SSL reverse proxy, and Cloudflare for DNS. No fluff — real commands, real config files, real decisions.

Key Takeaways

  • Ghost 5 runs in a lightweight Docker container — no system dependencies to install.
  • A single docker-compose.yml file defines your entire environment.
  • Your reverse proxy (Zoraxy or Nginx) handles HTTPS — Ghost doesn't need to know about certificates.
  • Data persists in /mnt/docker-data/ghost-blog/ and survives container updates.
  • Cloudflare DDNS + CNAME blog.botum.ca → botum.ca handles DNS resolution without a static IP.

Prerequisites

Before you start, make sure you have:

  • A Linux server (Ubuntu 22.04+ recommended) with SSH access
  • Docker and Docker Compose installed (docker compose v2)
  • A domain name — in this case blog.botum.ca via Cloudflare
  • A reverse proxy with SSL — here Zoraxy (Nginx, Traefik, or Caddy also work)
  • A non-root user with sudo access
  • Ports 80 and 443 open on your firewall

Step 1 — Create the Directory Structure

Ghost needs persistent storage for its files (themes, images, SQLite DB). Create the directory structure on the host:

mkdir -p /mnt/docker-data/ghost-blog/content
# The content folder will hold: data/, images/, themes/, adapters/

Why /mnt/? On this server, /mnt/ is mounted on a separate disk — Ghost data survives a system reinstall.

Docker - Container platform
Docker

Step 2 — Create docker-compose.yml

Create the Docker Compose config file. It defines the Ghost container, environment variables, and persistent volumes:

nano /mnt/docker-data/ghost-blog/docker-compose.yml
services:
  ghost-blog:
    image: ghost:5
    container_name: ghost-blog
    restart: unless-stopped
    ports:
      - "4598:4598"
    environment:
      url: https://blog.botum.ca
      NODE_ENV: production
    volumes:
      - /mnt/docker-data/ghost-blog/content:/var/lib/ghost/content
      - /mnt/docker-data/ghost-blog/config.production.json:/var/lib/ghost/config.production.json

Key points: restart: unless-stopped automatically restarts the container on reboot. The url variable must exactly match your final public URL with HTTPS — Ghost uses it to generate all internal links.

Step 3 — Create config.production.json

Ghost reads its configuration from this JSON file. Create it before starting the container:

{
  "url": "https://blog.botum.ca",
  "server": {
    "port": 4598,
    "host": "0.0.0.0"
  },
  "database": {
    "client": "sqlite3",
    "connection": {
      "filename": "/var/lib/ghost/content/data/ghost.db"
    }
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": ["stdout"]
  },
  "process": "local",
  "paths": {
    "contentPath": "/var/lib/ghost/content"
  }
}

Step 4 — Launch Ghost with Docker Compose

Start the container in detached mode:

cd /mnt/docker-data/ghost-blog
docker compose up -d
Docker commands in terminal
Running Ghost CMS with docker-compose up -d

Verify the container is running:

docker ps | grep ghost-blog
# Expected: ghost-blog   Up X minutes   0.0.0.0:4598->4598/tcp

# Check logs if needed:
docker logs ghost-blog --tail 50

Ghost runs database migrations on first start — wait 30–60 seconds before testing.

Step 5 — Configure Cloudflare DNS

In the Cloudflare dashboard, set up:

Cloudflare DNS configuration
Cloudflare
  • An A record for botum.ca → your server's public IP (enable Cloudflare Proxy ☁️)
  • A CNAME record blog → botum.ca (proxied)
  • Optional: automatic DDNS via a script or the Cloudflare DDNS addon if your IP changes

Result: blog.botum.ca resolves to your server's IP via Cloudflare, with free DDoS protection and CDN caching.

Step 6 — Configure Zoraxy as SSL Reverse Proxy

Zoraxy is a modern reverse proxy with a web UI and automatic SSL management (Let's Encrypt). In the Zoraxy interface, add a proxy rule:

  • Incoming domain: blog.botum.ca
  • Destination: http://localhost:4598 (or the server's internal IP)
  • SSL: enabled — Zoraxy automatically provisions the Let's Encrypt certificate
  • HTTPS redirect: enabled

Ghost runs on port 4598 as plain HTTP — Zoraxy handles TLS termination. Clean separation of concerns: Ghost does content, Zoraxy does security.

Ghost CMS - Modern publishing platform
Ghost CMS — a modern open-source publishing platform

Step 7 — First Login and Ghost Setup

Navigate to https://blog.botum.ca/ghost to create your admin account. Complete the setup wizard:

  • Blog name and description
  • Admin account (email + strong password)
  • Theme — the Source theme is installed by default on Ghost 5

Your Ghost blog is now live. Test public access at https://blog.botum.ca — you should see the Ghost homepage.

Useful Maintenance Commands

# Update Ghost to the latest 5.x version
docker compose pull && docker compose up -d

# Backup the SQLite database
cp /mnt/docker-data/ghost-blog/content/data/ghost.db ~/backup-ghost-$(date +%Y%m%d).db

# Follow logs in real time
docker logs ghost-blog -f

# Restart Ghost without losing data
docker compose restart ghost-blog

Conclusion

In under 15 minutes, you now have a self-hosted Ghost 5 blog, secured with HTTPS, publicly accessible via Cloudflare and Zoraxy. Your data is persistent, the container restarts automatically, and updates are two commands away.

Next steps: configure email sending (Mailgun or SMTP), install a custom theme, enable Ghost members and subscriptions, and set up automatic DB backups.

Take Action

If this tutorial helped you, subscribe to the BOTUM blog to get the next articles on self-hosting, Docker, and open-source tools. Questions about your setup? Drop a comment below — I respond to every message.

📥 Guide PDF complet

Téléchargez ce guide en PDF pour le consulter hors ligne.

⬇ Télécharger le guide (PDF)

🚀 Aller plus loin avec BOTUM

Ce guide couvre les bases. En production, chaque environnement a ses spécificités. Les équipes BOTUM accompagnent les organisations dans le déploiement, la configuration avancée et la sécurisation de leur infrastructure. Si vous avez un projet, parlons-en.

Discuter de votre projet →
📋 Proxmox Infrastructure Series: View complete series →
Proxmox Infrastructure Series