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: bridgeDeploying the Containers
After creating the docker-compose.yml file, I run:
docker compose up -dTo check the containers:
docker psTo view Ghost logs:
docker logs -f ghost_blogUsing 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/content2. Using the Wrong Ghost URL
Your Ghost URL must match your real domain:
url: https://yourdomain.comIf 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_blog4. Running Too Many Containers Without Monitoring
If you run many Ghost websites on one VPS, monitor:
docker statsThis 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.