Ghost Themes

How I Deploy Ghost with Docker

hodaifa
May 23, 2026
3 min read

Introduction

I use Ghost CMS for blogging, theme development, and running multiple websites.

For deployment, I prefer using:

  • Docker
  • Docker Compose
  • Nginx reverse proxy
  • VPS hosting
  • custom Ghost themes

This setup gives me more control, makes backups easier, and allows me to run multiple Ghost websites on the same server.

Why I Use Docker for Ghost

Docker makes Ghost deployment cleaner because each website can run inside its own container.

The main advantages are:

  • easier deployment
  • isolated environments
  • simple updates
  • better backup strategy
  • multiple Ghost websites on one VPS
  • cleaner development workflow

For a Ghost theme marketplace, this is very useful because I can test and deploy multiple themes quickly.

My Basic Ghost Docker Compose Setup

version: '3.8'

services:

  ghost:
    image: YOUR_IMAGE:latest
    container_name: GHOST_CONTAINER_NAME

    restart: always

    ports:
      - "HOST_PORT:2368"

    environment:
      security__staffDeviceVerification: "false"

      url: https://YOUR_DOMAIN.com

      database__client: mysql
      database__connection__host: mysql
      database__connection__user: root
      database__connection__password: YOUR_DB_PASSWORD
      database__connection__database: YOUR_DB_NAME

      NODE_ENV: production

    volumes:
      - GHOST_CONTENT_VOLUME:/var/lib/ghost/content

    depends_on:
      mysql:
        condition: service_healthy

    networks:
      - PROJECT_NETWORK

    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider http://localhost:2368 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 120s


  mysql:
    image: mysql:8.0

    container_name: MYSQL_CONTAINER_NAME

    restart: always

    environment:
      MYSQL_ROOT_PASSWORD: YOUR_DB_PASSWORD
      MYSQL_DATABASE: YOUR_DB_NAME

    volumes:
      - MYSQL_VOLUME:/var/lib/mysql

    networks:
      - PROJECT_NETWORK

    command: --default-authentication-plugin=mysql_native_password

    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pYOUR_DB_PASSWORD"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s


volumes:

  GHOST_CONTENT_VOLUME:
    name: GHOST_CONTENT_VOLUME
    driver: local

  MYSQL_VOLUME:
    name: MYSQL_VOLUME
    driver: local


networks:

  PROJECT_NETWORK:
    driver: bridge

Deploying the Containers

After creating the docker-compose.yml file, I run:

docker compose up -d

To check the containers:

docker ps

To view Ghost logs:

docker logs -f ghost_blog

Using Nginx as a Reverse Proxy

Ghost runs inside Docker on an internal port, but visitors access the site using a domain.

Example Nginx config:

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:2368;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Running Multiple Ghost Websites

One reason I like Docker is that I can run multiple Ghost sites on the same VPS.

Example:

ports:  - "3011:2368"

Another site can use:

ports:  - "3012:2368"

Then Nginx routes each domain or subdomain to the correct port.

Example:

server {
    listen 80;
    server_name blog-example.yourdomain.com;

    location / {
        proxy_pass http://127.0.0.1:3011;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

This is useful when managing:

  • blog themes
  • demo websites
  • client websites
  • marketplace previews

My Real Workflow

In my own setup, I use a Contabo VPS with multiple Ghost websites.

Each website usually has:

  • one Ghost container
  • one MySQL database
  • one content volume
  • one Nginx route
  • one domain or subdomain

This helps me keep projects organized and easier to maintain.

Backing Up Ghost Docker Volumes

Backups are very important.

For Ghost content:

docker run --rm -v ghost_content:/source -v /opt:/backup  alpine tar czf /backup/ghost_backup.tar.gz -C /source .

For MySQL data:

docker run --rm -v ghost_mysql_data:/source  -v /opt:/backup alpine tar czf /backup/ghost_mysql_backup.tar.gz -C /source .

Common Mistakes to Avoid

1. Forgetting Persistent Volumes

If you do not use volumes, your Ghost content can be lost when containers are recreated.

Always use:

volumes:  - ghost_content:/var/lib/ghost/content

2. Using the Wrong Ghost URL

Your Ghost URL must match your real domain:

url: https://yourdomain.com

If this is wrong, you may get broken links, wrong redirects, or admin issues.


3. Not Checking Logs

When Ghost has a problem, logs usually show the reason.

Use:

docker logs -f ghost_blog

4. Running Too Many Containers Without Monitoring

If you run many Ghost websites on one VPS, monitor:

docker stats

This helps you check CPU, RAM, and container usage.

Final Thoughts

Deploying Ghost with Docker is one of the best approaches if you want control, flexibility, and scalability.

For one website, it keeps deployment clean.

For multiple websites, it becomes even more powerful.

If you build Ghost themes, run demos, or manage several publications, Docker makes the workflow much easier.