How to Host Multiple Ghost Blogs on a Single Server with Docker and Nginx
This guide demonstrates how to host multiple Ghost blogs on a single Ubuntu server using Docker, Nginx as a reverse proxy, and MySQL as the database backend. We’ll also secure the setup with Let’s Encrypt for HTTPS.
In this example, we’ll host two Ghost blogs on the following subdomains:
- Blog 1:
blog.mydomain1.com
- Blog 2:
blog.mydomain2.com
This setup is scalable, allowing you to add more blogs by extending the configuration.
Step 1: Prepare Your Server
- Update Your Server Ensure your server is updated and ready:
sudo apt update && sudo apt upgrade -y
- Install Docker and Docker Compose Install the necessary tools to run Docker containers:
sudo apt install docker.io -y
sudo apt install docker-compose -y
- Set Up a Working Directory Create a directory to hold your configurations:
mkdir docker-dir
cd docker-dir
Step 2: Configure Docker Compose
Create a docker-compose.yml
file to define your services. Open the file for editing:nano docker-compose.yml
Insert the following configuration:
version: "3.1"
services:
reverse-proxy:
image: nginx
restart: always
container_name: reverse-proxy
volumes:
- /root/docker-dir/nginx.conf:/etc/nginx/nginx.conf
- /etc/letsencrypt:/etc/letsencrypt
- /etc/ssl:/etc/ssl
ports:
- 80:80
- 443:443
ghost-blog1:
image: ghost:latest
restart: always
container_name: ghost-blog1
ports:
- 2368:2368
volumes:
- ./ghost-blog1/content:/var/lib/ghost/content
environment:
NODE_ENV: production
database__client: mysql
database__connection__host: mysql-ghost-db
database__connection__user: root
database__connection__password: <password>
database__connection__database: ghost_blog1
url: https://blog.mydomain1.com
ghost-blog2:
image: ghost:latest
restart: always
container_name: ghost-blog2
ports:
- 2369:2368
volumes:
- ./ghost-blog2/content:/var/lib/ghost/content
environment:
NODE_ENV: production
database__client: mysql
database__connection__host: mysql-ghost-db
database__connection__user: root
database__connection__password: <password>
database__connection__database: ghost_blog2
url: https://blog.mydomain2.com
mysql-ghost-db:
image: mysql:8.0
restart: always
container_name: mysql-ghost-db
environment:
MYSQL_ROOT_PASSWORD: <password>
volumes:
- ghost-database:/var/lib/mysql
volumes:
ghost-database:
Save and exit the file.
Step 3: Configure Nginx as a Reverse Proxy
Create an Nginx configuration file to route traffic to the correct Ghost container. Open the file:nano nginx.conf
Insert the following configuration:
events {
worker_connections 1024;
}
http {
# Redirect HTTP to HTTPS for blog.mydomain1.com
server {
listen 80;
server_name blog.mydomain1.com;
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS server block for blog.mydomain1.com
server {
listen 443 ssl;
server_name blog.mydomain1.com;
ssl_certificate /etc/letsencrypt/live/blog.mydomain1.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.mydomain1.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://ghost-blog1: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 https;
}
}
# Redirect HTTP to HTTPS for blog.mydomain2.com
server {
listen 80;
server_name blog.mydomain2.com;
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS server block for blog.mydomain2.com
server {
listen 443 ssl;
server_name blog.mydomain2.com;
ssl_certificate /etc/letsencrypt/live/blog.mydomain2.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.mydomain2.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://ghost-blog2: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 https;
}
}
}
Save and exit the file.
Step 4: Set Up HTTPS with Let’s Encrypt
Install Certbot and generate SSL certificates:
sudo apt install certbot python3-certbot-nginx -y
sudo certbot certonly --webroot -w /var/lib/docker/volumes -d blog.mydomain1.com -d blog.mydomain2.com
Reload the Nginx service: sudo systemctl reload nginx
Step 5: Configure DNS
Add the following DNS A records to your domain provider:
blog.mydomain1.com → [Your Server IP]
blog.mydomain2.com → [Your Server IP]
Step 6: Start the Services
Start your services using Docker Compose: docker-compose up -d
Wrapping Up
You now have a robust setup for hosting multiple Ghost blogs on a single server. This configuration uses Docker, MySQL for data storage, and Nginx as a reverse proxy, all secured with SSL from Let’s Encrypt. You can scale this by adding more Ghost services and updating the Nginx configuration.