Ghost Installation — Full Production Server Setup with Nginx and SSL
In this tutorial, you'll set up Ghost on a production server using the Ghost CLI — configuring Nginx as a reverse proxy, securing the site with Let's Encrypt SSL certificates, setting up MySQL, and managing the Ghost process with systemd.
What You'll Learn
- Production server requirements for Ghost (CPU, RAM, OS)
- Installing Node.js and MySQL on a Linux server
- Configuring DNS for your Ghost site
- Running the production Ghost installer step by step
- Setting up Nginx as a reverse proxy
- Securing the site with Let's Encrypt SSL
- Configuring systemd for process management
- Post-installation verification and testing
- Common production issues and how to fix them
Why It Matters
Local development is for experimentation. Production deployment is where your site goes live for the world to see. The production setup involves several components — Node.js, MySQL, Nginx, SSL, email — that must work together correctly. A single misconfiguration can make your site slow, insecure, or completely inaccessible. Following a proven installation process saves hours of debugging.
Real-World Use
A startup launches a company blog using Ghost. The developer provisions a $20/month VPS on DigitalOcean, runs sudo ghost install, enters the domain name and database credentials, and the CLI configures everything automatically — Nginx, SSL from Let's Encrypt, systemd service, and email transport. Twenty minutes later, the site is live at blog.company.com with HTTPS, automatic restarts, and daily log rotation.
Learning Path
flowchart LR A["Ghost Editor"] --> B["Ghost Installation
You are here"]:::current B --> C["Ghost Config"] C --> D["Ghost Admin"] D --> E["Ghost Labs"] classDef current fill:#38bdf8,color:#0f172a,stroke-width:2px
Server Requirements
Before installing Ghost in production, ensure your server meets these minimum requirements.
Hardware
| Component | Minimum | Recommended |
|---|---|---|
| RAM | 1 GB | 2 GB+ |
| CPU | 1 core | 2 cores |
| Storage | 10 GB | 20 GB+ (SSD) |
| Swap | 1 GB | 2 GB |
Software
| Software | Version |
|---|---|
| OS | Ubuntu 20.04+, Debian 11+, or CentOS 8+ |
| Node.js | 18.x or 20.x LTS |
| MySQL | 8.0.13+ or MariaDB 10.4+ |
| Nginx | 1.18+ (for reverse proxy) |
| Systemd | Included with modern Linux |
Network
- A domain name pointing to your server's IP address
- Port 80 (HTTP) and 443 (HTTPS) open in your firewall
- Port 25 open for email sending (if using your own mail server)
Step 1: Provision a Server
Create a VPS with your hosting provider. The Ghost CLI works best on Ubuntu 22.04 or 24.04 LTS. Connect via SSH:
ssh root@your-server-ip
Update the system packages:
sudo apt update && sudo apt upgrade -y
Step 2: Install Node.js
Ghost requires Node.js 18.x or 20.x LTS. Use the NodeSource Repository for the correct version:
# Add NodeSource repository for Node.js 20.x
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
# Install Node.js
sudo apt install -y nodejs
# Verify installation
node --version
# v20.x.x
npm --version
# 10.x.x
Step 3: Install MySQL
Ghost 5.x requires MySQL 8.0+ or MariaDB 10.4+. Install MySQL:
sudo apt install -y mysql-server
# Secure the installation
sudo mysql_secure_installation
After installation, create a database and user for Ghost:
sudo mysql
-- Create database
CREATE DATABASE ghost_production CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- Create user and grant privileges
CREATE USER 'ghost_user'@'localhost' IDENTIFIED BY 'your-strong-password';
GRANT ALL PRIVILEGES ON ghost_production.* TO 'ghost_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Why utf8mb4? This character set supports the full Unicode range, including emoji and special characters used in modern content.
Step 4: Install Nginx
Nginx acts as a reverse proxy — it receives requests from the internet, forwards them to the Ghost Node.js process, and returns the responses to the client.
sudo apt install -y nginx
# Allow HTTP and HTTPS through the firewall
sudo ufw allow 'Nginx Full'
# Verify Nginx is running
sudo systemctl status nginx
Step 5: Configure DNS
Create an A record for your domain pointing to your server's IP address:
Type: A
Name: blog (or @ for the root domain)
Value: your-server-ip
TTL: 300 (5 minutes)
If you are using a subdomain like blog.example.com, create:
Type: CNAME
Name: blog
Value: your-server-ip
TTL: 300
Wait for DNS propagation (usually 1-10 minutes for low TTL values).
Step 6: Install Ghost CLI
sudo npm install -g ghost-cli@latest
Step 7: Create a Ghost Directory
Ghost should not be installed in the web root like /var/www/html. Instead, create a dedicated directory:
sudo mkdir -p /var/www/ghost
sudo chown -R $USER:$USER /var/www/ghost
cd /var/www/ghost
Step 8: Run the Production Installer
ghost install
The CLI asks a series of questions. Here is what to expect:
? Enter your blog URL: https://blog.example.com
? Enter your MySQL hostname: localhost
? Enter your MySQL username: ghost_user
? Enter your MySQL password: [your-password]
? Enter your Ghost database name: ghost_production
? Enter a Ghost database username (optional): [press Enter to skip]
? Set up a Ghost MySQL user? [Y/n] n
? Do you wish to set up Nginx? Y
? Do you wish to set up SSL? Y
? Enter your email: admin@example.com
? How would you like to set up SSL? Let's Encrypt (recommended)
? Do you wish to set up Systemd? Y
? Do you want to start Ghost? Y
The installer then:
- Downloads and installs Ghost
- Creates the MySQL database tables
- Configures Nginx as a reverse proxy
- Obtains an SSL certificate from Let's Encrypt
- Sets up a systemd service for auto-start
- Starts the Ghost process
On success, you see:
✔ Setting up Systemd
✔ Creating systemd service file
✔ Starting Ghost
Ghost is running in production mode at https://blog.example.com
Visit your domain. You should see the default Ghost welcome page.
Understanding the Nginx Configuration
The CLI creates an Nginx configuration file at /etc/nginx/sites-available/blog.example.com. Here is what it contains:
server {
listen 80;
server_name blog.example.com;
# Redirect HTTP to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name blog.example.com;
# SSL configuration (managed by Certbot)
ssl_certificate /etc/letsencrypt/live/blog.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.example.com/privkey.pem;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Proxy requests to Ghost
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 $host;
proxy_pass http://127.0.0.1:2368;
}
# Static assets caching
location ~ ^/(ghost|content)/ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# WebSocket support for Ghost admin
location /ghost/api/admin/ {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:2368;
}
}
The key lines are:
proxy_pass http://127.0.0.1:2368— forwards all requests to Ghost running on port 2368- The SSL certificate paths point to Let's Encrypt certificates
- The
Cache-Controlheader caches static assets for 30 days - The Websocket configuration supports real-time admin features
Understanding the systemd Service
The CLI creates a systemd service file at /etc/systemd/system/ghost_blog.example.com.service:
[Unit]
Description=Ghost systemd service for blog.example.com
Documentation=https://ghost.org/docs/
After=network.target
[Service]
Type=simple
WorkingDirectory=/var/www/ghost
User=ghost
Group=ghost
Environment=NODE_ENV=production
ExecStart=/usr/bin/node /var/www/ghost/current/index.js
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
This service:
- Runs Ghost as a dedicated
ghostuser (not root) for security - Sets
NODE_ENV=production - Restarts Ghost automatically if it crashes
- Starts automatically when the server reboots
You can manage the service manually:
# Check service status
sudo systemctl status ghost_blog.example.com
# Restart Ghost
sudo systemctl restart ghost_blog.example.com
# View logs
sudo journalctl -u ghost_blog.example.com -f
Post-Installation Verification
After installation, verify everything is working:
# Check Ghost is running
ghost status
# Check Nginx configuration is valid
sudo nginx -t
# Verify SSL certificate
sudo certbot certificates
# Test the site is accessible
curl -I https://blog.example.com
Expected output from curl -I:
HTTP/2 200
server: nginx
content-type: text/html; charset=utf-8
strict-transport-security: max-age=31536000; includeSubDomains
Adding Swap Space
If your server has less than 2 GB RAM, add swap space to prevent out-of-memory errors:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
# Make swap permanent
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Ghost with Node.js can use significant memory during build operations (asset compilation, image processing).
Common Mistakes
Installing Ghost as root: The installer creates a dedicated
ghostuser automatically, but runningghost installas root without sudo can cause permission issues later. Always usesudo ghost install.Choosing a VPS with insufficient RAM: Ghost needs at least 1 GB RAM at minimum. On a 512 MB server, Ghost will run out of memory during asset compilation or when traffic spikes. Choose at least 2 GB for a production site.
Forgetting to configure the firewall: If ports 80 and 443 are not open, visitors cannot reach your site. Configure
ufwor your cloud provider's firewall before running the installer.Using an incorrect MySQL collation: The database must use
utf8mb4_unicode_cior a similar Unicode collation. Using the defaultlatin1_swedish_cicauses issues with special characters in content.Skipping the SSL setup: The installer offers to skip SSL. Never skip this for production sites. Without SSL, browsers mark your site as "Not Secure," and search engines rank it lower. Let's Encrypt certificates are free and automatic.
Practice Questions
What is the purpose of Nginx in a Ghost production setup? Answer: Nginx acts as a reverse proxy — it receives HTTP/HTTPS requests from the internet, handles SSL encryption, serves static files efficiently, sets security headers, and forwards dynamic requests to the Ghost Node.js process running on port 2368.
Why does Ghost create a dedicated system user during installation? Answer: Running Ghost under a dedicated system user (not root) is a security best practice. If the Ghost process is compromised, the attacker has limited permissions — they cannot modify system files, read other users' data, or install software.
How do you check the Ghost application logs on a production server? Answer: Use
sudo journalctl -u ghost_blog.example.com -fto view the systemd service logs in real time. Alternatively, check the Ghost log file atcontent/logs/within the Ghost installation directory.Challenge: Set up a production Ghost server from scratch on a VPS. Document every step with screenshots and commands. Include the MySQL database creation, Nginx configuration, SSL setup, and the systemd service. Then access the site, create a test post, and verify the site appears on the public internet with HTTPS.
FAQ
Mini Project
Your task: Deploy Ghost on a production VPS and create a deployment checklist.
- Provision a VPS with Ubuntu 24.04.
- Install Node.js 20.x, MySQL 8.0, and Nginx.
- Set up DNS for your domain.
- Run
sudo ghost installwith the production configuration. - After successful installation, verify the site loads over HTTPS.
- Create a deployment checklist with at least 15 verification points covering: DNS, SSL, Nginx, systemd, database, firewall, backups, monitoring, email, and security headers.
This exercise gives you a repeatable deployment process you can use for every future Ghost project.
What's Next
Now that your Ghost site is live in production, it is time to fine-tune the configuration:
Continue to Lesson 6: Ghost Config — Configure config.production.json, URL settings, mail transport, and database options.
Related lessons:
- Ghost CLI — Manage your Ghost installation
- Ghost Admin — Admin dashboard settings guide
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro