Ghost Setup: Self-Hosted Blog and Content Platform
Ghost Docker installation, Nginx reverse proxy, SSL certificate, theme setup, membership and subscription system, email newsletter and production configuration.
Table of Contents
Ghost Setup: Self-Hosted Blog and Content Platform
Ghost is an open-source CMS platform designed for modern blogging and content publishing. Compared to WordPress, it's faster, more secure, and content-focused. It also supports content monetization with membership system, newsletter, and payment integration.
What is Ghost?
Ghost is a Node.js-based headless CMS and publishing platform. Key features:
- Speed: Node.js-based, very fast page loading
- SEO: Built-in SEO tools and structured data
- Membership: Free and paid membership system
- Newsletter: Built-in email newsletter sending
- Themes: Handlebars-based theme system
- API: REST API for headless CMS usage
System Requirements
- Server with Docker and Docker Compose installed
- Minimum 1 GB RAM (2 GB recommended)
- MySQL 8.0 or SQLite
- Valid domain and SSL certificate
Docker Installation
Production Setup with MySQL
# docker-compose.yml
version: '3.8'
services:
ghost:
image: ghost:5-alpine
container_name: ghost
restart: unless-stopped
depends_on:
- db
environment:
database__client: mysql
database__connection__host: db
database__connection__user: ghost
database__connection__password: strong-password
database__connection__database: ghost
url: https://blog.example.com
mail__transport: SMTP
mail__options__host: smtp.mailgun.org
mail__options__port: 587
mail__options__auth__user: postmaster@mg.example.com
mail__options__auth__pass: mailgun-api-key
mail__from: noreply@example.com
volumes:
- ./ghost-content:/var/lib/ghost/content
ports:
- "127.0.0.1:2368:2368"
db:
image: mysql:8.0
container_name: ghost-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: root-password
MYSQL_DATABASE: ghost
MYSQL_USER: ghost
MYSQL_PASSWORD: strong-password
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
# Start containers
docker compose up -d
# Check logs
docker logs ghost -f
Ghost automatically creates the database on first start. Access the admin panel at https://blog.example.com/ghost.
Quick Start with SQLite
services:
ghost:
image: ghost:5-alpine
container_name: ghost
restart: unless-stopped
environment:
url: https://blog.example.com
NODE_ENV: production
volumes:
- ./ghost-content:/var/lib/ghost/content
ports:
- "127.0.0.1:2368:2368"
Nginx Reverse Proxy and SSL
server {
listen 80;
server_name blog.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name blog.example.com;
ssl_certificate /etc/letsencrypt/live/blog.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
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;
proxy_buffering off;
}
}
# Get SSL certificate
certbot --nginx -d blog.example.com
Admin Panel Configuration
Go to https://blog.example.com/ghost:
- Create admin account (name, email, password)
- Enter site title and description
- Select or upload a theme
- Configure publication settings
Membership and Subscription System
Membership Configuration
Settings → Membership
- Allow free members: Enabled
- Paid subscriptions: Via Stripe
- Default post access: Public / Members only / Paid
Stripe Integration
Settings → Membership → Stripe
1. Create a Stripe account
2. Enter API keys:
- Publishable key: pk_live_...
- Secret key: sk_live_...
3. Add webhook endpoint:
https://blog.example.com/members/webhooks/stripe
Content Access Control
This section is public.
<!--members-only-->
This section is visible only to members.
<!--paid-members-only-->
This section is visible only to paid members.
Email Newsletter
Mailgun Configuration
environment:
mail__transport: SMTP
mail__options__service: Mailgun
mail__options__host: smtp.mailgun.org
mail__options__port: 587
mail__options__auth__user: postmaster@mg.example.com
mail__options__auth__pass: mailgun-smtp-password
mail__from: "Blog Name <noreply@example.com>"
Backup
# Backup Ghost content directory
tar -czf ghost-backup-$(date +%Y%m%d).tar.gz ./ghost-content/
# MySQL database backup
docker exec ghost-db mysqldump -u ghost -pstrong-password ghost > \
ghost-db-$(date +%Y%m%d).sql
# Automated backup script
cat > /etc/cron.daily/ghost-backup << 'EOF'
#!/bin/bash
BACKUP_DIR="/backups/ghost"
mkdir -p $BACKUP_DIR
tar -czf $BACKUP_DIR/content-$(date +%Y%m%d).tar.gz /opt/ghost/ghost-content/
docker exec ghost-db mysqldump -u ghost -pstrong-password ghost | \
gzip > $BACKUP_DIR/db-$(date +%Y%m%d).sql.gz
find $BACKUP_DIR -name "*.gz" -mtime +30 -delete
EOF
chmod +x /etc/cron.daily/ghost-backup
With REXE VPS servers, you can set up a professional blog and content platform with Ghost. 2 GB RAM can serve thousands of readers.
Conclusion
Ghost is a powerful, modern alternative to WordPress for content publishing. Its built-in membership system, newsletter capabilities, and clean editor make it ideal for professional bloggers and publishers. With Docker deployment on REXE servers, you can have a production-ready Ghost instance running in minutes.
Related Articles
Coolify Setup: Application Deployment with Self-Hosted PaaS
Coolify installation, Docker and Git integration, application deployment, database management, SSL certificates, environment variables, and self-hosted PaaS alternative to Heroku/Netlify.
Nextcloud Setup: Self-Hosted Cloud Storage and Collaboration
Nextcloud installation with Docker, Apache/Nginx configuration, database setup, SSL certificate, file synchronization, app store, and performance optimization.
Docker Management with Portainer: Web-Based Container Control
Portainer CE installation, Docker and Kubernetes management, stack deployment, volume/network management, user authorization, webhook and GitOps integration.