Skip to main content
Back to Category

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.

Read time: 13 dk Self-Hosting
ghostblogcmsself-hostingdockernewslettercontent management

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

hljs yaml
# 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:
hljs bash
# 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

hljs yaml
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

hljs nginx
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;
    }
}
hljs bash
# Get SSL certificate
certbot --nginx -d blog.example.com

Admin Panel Configuration

Go to https://blog.example.com/ghost:

  1. Create admin account (name, email, password)
  2. Enter site title and description
  3. Select or upload a theme
  4. 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

hljs markdown
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

hljs yaml
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

hljs bash
# 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.