How to Host Ghost CMS with MySQL and Nginx on Ubuntu VPS (Step-by-Step)

For independent journalists, technical bloggers, and digital media publications, Ghost CMS has emerged as the premier modern alternative to WordPress. Engineered entirely in Node.js with a lightning-fast Ember/React administrative interface and a clean Handlebars templating engine, Ghost is built from the ground up for high-velocity publishing, native newsletter subscriptions, and paid membership monetization.

While the official managed Ghost Pro platform charges escalating fees based on subscriber counts, self-hosting Ghost CMS on an optimized Linux VPS delivers unmetered subscriber capacity, complete data sovereignty, and sub-50ms response times at fixed hardware costs. This tutorial walks you through server preparation, Node.js runtime installation, MySQL database configuration, Ghost-CLI automated deployment, and production Nginx reverse proxy tuning.

1. Server Hardware Sizing & Baseline Prerequisites

To ensure responsive performance during traffic surges, ensure your VPS meets these baseline specifications:

  • Operating System: Ubuntu 24.04 LTS or 22.04 LTS (clean minimal install).
  • Memory: Minimum 1GB RAM (2GB+ recommended to prevent Node.js garbage collection memory spikes during newsletter dispatch).
  • Database: MySQL 8.0 Server (Ghost strictly requires MySQL 8 in production; MariaDB is not officially supported by upstream Ghost-CLI).
  • Web Server: Nginx reverse proxy terminating SSL on ports 80 and 443.

2. Installing Node.js LTS, Nginx & MySQL 8

Ghost requires an active Node.js LTS release (Node.js 18 or 20). Install dependencies via NodeSource:

# Update repository cache
sudo apt update && sudo apt upgrade -y && sudo apt install -y curl nginx mysql-server ufw

# Import NodeSource Node.js 20 LTS repository
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Verify versions
node -v && npm -v
# Output confirms Node.js v20.x and npm 10.x

Run sudo mysql_secure_installation to lock down database security, then initialize a dedicated database and user for Ghost:

sudo mysql -u root -p << 'EOF'
CREATE DATABASE ghost_production CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'ghost_user'@'localhost' IDENTIFIED BY 'StrongRandomGhostPassword2026!';
GRANT ALL PRIVILEGES ON ghost_production.* TO 'ghost_user'@'localhost';
FLUSH PRIVILEGES;
EOF

3. Installing Ghost-CLI & System Preparation

Ghost strictly forbids running commands as the root user. Create a dedicated system user and install the official Ghost command-line interface globally:

# Install Ghost-CLI globally
sudo npm install -g ghost-cli@latest

# Create non-root user and assign sudo privileges
sudo adduser --gecos "" ghostuser
sudo usermod -aG sudo ghostuser

# Create target web root directory
sudo mkdir -p /var/www/ghost
sudo chown ghostuser:ghostuser /var/www/ghost
sudo chmod 775 /var/www/ghost

4. Executing Automated Ghost Production Installation

Switch to your unprivileged user account and execute the interactive Ghost installation wizard:

su - ghostuser
cd /var/www/ghost
ghost install

The Ghost-CLI will prompt you for configuration details:

  • Blog URL: https://yourdomain.com
  • MySQL hostname: localhost
  • MySQL username: ghost_user
  • MySQL password: StrongRandomGhostPassword2026!
  • Ghost database name: ghost_production
  • Set up a ‘ghost’ mysql user?no (already configured)
  • Set up Nginx?yes (automatically writes optimized server blocks)
  • Set up SSL?yes (automatically provisions Let’s Encrypt certificates)
  • Set up systemd service?yes (configures auto-start on boot)
  • Start Ghost?yes

5. Tuning Nginx Reverse Proxy for Ghost

Ghost-CLI automatically generates an Nginx configuration file in /etc/nginx/sites-available/yourdomain.com.conf. To ensure large image and theme uploads do not fail with HTTP 413 errors, verify that client_max_body_size is set properly:

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    # Allow large media uploads
    client_max_body_size 50M;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_pass http://127.0.0.1:2368;
    }
}

Navigate to https://yourdomain.com/ghost to complete initial administrator registration and launch your modern publication.

5. Configuring Transactional Email with Mailgun & Ghost Custom Routing

Ghost relies strictly on external transactional email services to handle member newsletters, signup verification links, and staff invitations. Sending email directly from local Sendmail or Postfix almost always results in spam folder placement. Mailgun is the officially recommended standard for Ghost instances:

  • Configuring config.production.json: Open /var/www/ghost/config.production.json and add your Mailgun SMTP credentials under the mail object:
    "mail": {
      "transport": "SMTP",
      "options": {
        "service": "Mailgun",
        "host": "smtp.mailgun.org",
        "port": 587,
        "secure": false,
        "auth": {
          "user": "[email protected]",
          "pass": "your_mailgun_api_smtp_key"
        }
      }
    }
  • Restarting Ghost Daemon: Execute ghost restart inside /var/www/ghost to apply the updated mail configuration. Navigate to Settings > Email newsletter in the Ghost Admin dashboard and send a test newsletter to verify DKIM, SPF, and DMARC alignment.
  • Custom Dynamic Routing (routes.yaml): Ghost allows custom taxonomic structures, podcast feeds, and membership paywalls via routes.yaml. Download your default routing file from Settings > Labs > Routes, configure custom collections (e.g., separating technical tutorials from company announcements), and upload the YAML file without restarting the Node.js server.

6. Automated Daily MySQL Backups & Image Optimization

Ensure business continuity by automating daily database dumps and media synchronization to secure cloud storage:

# Create backup script /opt/backup_ghost.sh
mysqldump -u ghost_user -p'YourStrongPassword' ghost_production | gzip > /backups/ghost_db_$(date +\%F).sql.gz
tar -czf /backups/ghost_content_$(date +\%F).tar.gz /var/www/ghost/content/images/

Schedule this script to run daily at 02:00 AM via crontab to maintain redundant point-in-time recovery archives.

Host Ghost CMS on High-Speed CpanelFree VPS

Unmetered members, unlimited newsletters, and pure NVMe performance. Deploy Ghost CMS on reliable, high-performance cloud infrastructure with CpanelFree.

Discover CpanelFree Cloud VPS →

Leave a Comment