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
Author
REXE Teknoloji Network & Security Team
Editor
REXE Teknoloji Technical Editorial
First published
Last updated

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.

Frequently Asked Questions

I can't access the Ghost admin panel, getting a 404 error.

Check that the Ghost container is running with docker ps. Make sure the URL environment variable is set correctly (https://blog.example.com). Check the Nginx proxy configuration. Review Ghost logs: docker logs ghost. For first setup, go to the /ghost path: https://blog.example.com/ghost.

Email sending isn't working, newsletters can't be sent.

Check SMTP settings. If using Mailgun, make sure the domain is verified. Send a test email from Ghost admin panel Settings → Email. In Mailgun sandbox mode, you can only send to verified addresses. Activate the Mailgun domain for production.

Stripe payment integration isn't working.

Check that Stripe API keys are entered correctly (live key, not test key). Verify the webhook endpoint is added in the Stripe dashboard. Make sure the Ghost URL is HTTPS (Stripe rejects HTTP). Check webhook events from Stripe logs.

How do I update Ghost?

Back up before updating (content directory + database). Pull the new image with docker compose pull. Restart the container with docker compose up -d. Ghost automatically runs database migrations. For major version updates (e.g., 4.x → 5.x), read the release notes and test in a staging environment.

I uploaded a custom theme but it's not showing.

Make sure the theme .zip file has the correct structure (package.json and index.hbs are required). Activate the theme via Settings → Design → Activate. Check Ghost logs for theme errors. The theme must be compatible with your Ghost version (you can check with gscan).

Ghost is very slow, page load times are long.

If using MySQL, optimize the database connection. Add gzip compression and browser caching in Nginx. Enable caching in Ghost Settings → Labs. Consider using a CDN (Cloudflare free plan). Increase server RAM (minimum 2 GB recommended).

Related Articles

Network TrafficInbound GbpsOutbound Gbps