How to Deploy Ghost on Ubuntu with Nginx and SSL in 15 Minutes

This detailed tutorial will take you through the steps of installing Ghost CMS on Ubuntu 20.04/22.04 using Nginx as a reverse proxy and an SSL certificate to the same to give you secure access to HTTPS.
Prerequisites
- Ubuntu 20.04/ 22.04 server
- A server with at least 1GB memory (2GB is recommended)
- NodeJS 20
- MySQL 8
- Systemd
- Root/sudo access
- Domain name that will refer to your server IP address
Step 1: Update System and Install Dependencies
# Update system packages
sudo apt update && sudo apt upgrade -y
# Install Node.js 20.x (Ghost requirement)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Install additional dependencies
sudo apt install -y nginx mysql-server build-essential
Step 2: Create Ghost User
Ghost requires a non-root user for security. Create a dedicated user:
# Create ghost user with home directory
sudo adduser ghost --disabled-password --gecos "Ghost CMS"
# Add ghost user to sudo group (needed for Ghost CLI)
sudo usermod -aG sudo ghost
# Switch to ghost user
sudo su - ghost
Step 3: Install Ghost CLI
# Install ghost CLI as a global (as ghost user)
sudo npm install ghost-cli@latest -g
# Create directory for Ghost installation
sudo mkdir -p /var/www/your_sitename
sudo chown ghost:ghost /var/www/your_sitename
sudo chmod 775 /var/www/your_sitename
Step 4: Configure MySQL Database
# Secure MySQL installation (run as root user)
exit # Exit ghost user back to root
sudo mysql_secure_installation
# Create Ghost database and user
sudo mysql -u root -p
In the MySQL prompt, run:
CREATE DATABASE your_sitename_prod;
CREATE USER 'ghost'@'localhost' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON your_sitename_prod.* TO 'ghost'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Switch back to ghost user:
sudo su - ghost
Step 5: Install and Configure Ghost
# Navigate to Ghost directory (as ghost user)
cd /var/www/your_sitename
# Install Ghost
ghost install
# Follow the prompts:
# - Blog URL: https://yourdomain.com
# - MySQL hostname: localhost
# - MySQL username: ghost
# - MySQL password: your_strong_password
# - Database name: your_sitename_prod
# - Set up Nginx: Yes
# - Set up SSL: Yes
# - Set up systemd: Yes
# - Start Ghost: Yes
Note: With a user that has sudo privileges, a Ghost CLI will configure automatically Nginx and set it up with SSL.
Step 5: Configure Nginx (Manual Setup, if Needed)
If you need to manually configure Nginx, first create a server block:
sudo nano /etc/nginx/sites-available/your_sitename
Add the following configuration:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com www.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
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;
}
location ~ /.well-known {
allow all;
}
client_max_body_size 50m;
}
Enable the site:
sudo ln -s /etc/nginx/sites-available/your_sitename /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 6: Configure SSL with Let's Encrypt
If SSL wasn't set up automatically during Ghost installation:
# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Obtain SSL certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Test automatic renewal
sudo certbot renew --dry-run
Step 7: Configure Firewall
# Enable UFW firewall
sudo ufw enable
# Allow necessary ports
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw status
Step 8: Ghost Configuration and Management
Basic Ghost Commands
# Navigate to Ghost directory (as ghost user)
cd /var/www/your_sitename
# Start Ghost
ghost start
# Stop Ghost
ghost stop
# Restart Ghost
ghost restart
# Check Ghost status
ghost status
# Update Ghost
ghost update
# View logs
ghost log
Configuration File
Edit Ghost configuration if needed:
nano /var/www/your_sitename/config.production.json
Example configuration:
{
"url": "https://yourdomain.com",
"server": {
"port": 2368,
"host": "127.0.0.1"
},
"database": {
"client": "mysql",
"connection": {
"host": "localhost",
"user": "ghost",
"password": "your_password",
"database": "ghost_prod"
}
},
"mail": {
"transport": "Direct"
},
"logging": {
"transports": ["file", "stdout"]
},
"process": "systemd",
"paths": {
"contentPath": "/var/www/ghost/content"
}
}
Step 9: Post-Installation Setup
Access Ghost Admin
- Navigate to
https://yourdomain.com/ghost
- Create your admin account
- Configure your site settings
Performance Optimization
# Enable Nginx gzip compression
sudo nano /etc/nginx/nginx.conf
Add to the http block:
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private must-revalidate auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss;
Set up Automatic Backups
Create a backup script:
sudo nano /usr/local/bin/your_sitename_backup.sh
#!/bin/bash
BACKUP_DIR="/backup/your_sitename"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p $BACKUP_DIR
# Backup Ghost content
tar -czf $BACKUP_DIR/your_sitename-content-$DATE.tar.gz /var/www/your_sitename/content
# Backup database
mysqldump -u ghost -p your_sitename_backup_prod > $BACKUP_DIR/your_sitename_db_$DATE.sql
# Keep only last 7 days of backups
find $BACKUP_DIR -name "your_sitename-*" -mtime +7 -delete
Make it executable and add to crontab:
sudo chmod +x /usr/local/bin/your_sitename_backup.sh
sudo crontab -e
# Add this line for daily backups at 2 AM
0 2 * * * /usr/local/bin/your_sitename_backup.sh
Troubleshooting
Common Commands
# Check Ghost status
sudo systemctl status ghost_yourdomain-com
# View Ghost logs
sudo journalctl -u ghost_yourdomain-com -f
# Check Nginx status
sudo systemctl status nginx
# Test Nginx configuration
sudo nginx -t
# Check SSL certificate
sudo certbot certificates
Common Issues
- Ghost won't start: Check database connectivity and file permissions
- 502 Bad Gateway: Ensure Ghost is running on port 2368
- SSL issues: Verify certificate paths in Nginx configuration
- Performance issues: Consider upgrading server resources or enabling caching
Security Best Practices
- User Security: Ghost runs as a non-root user for security isolation
- Regular Updates: Keep Ghost, Ubuntu, and dependencies updated
- Strong Passwords: Select complex passwords of all accounts
- Firewall: restrict only the needed ports
- Backups: Apply automated frequent data backup
- Monitoring: Configure server check up and notifications
- SSL: Keep SSL certificates renewed automatically
Important Security Notes
- Never run Ghost as root: Ghost CLI will refuse to install as root user
- Ghost user permissions: The ghost user needs sudo access only for initial setup and updates
- File permissions: Ghost files should be owned by the ghost user
- Service isolation: Running Ghost as a separate user isolates it from system processes
Maintenance
- Monthly: Update Ghost and system packages
- Weekly: Check backup integrity
- Daily: Monitor server resources and logs
- As needed: Review and update security configurations
Your Ghost blog is now successfully deployed with Nginx and SSL! Access your admin panel at https://yourdomain.com/ghost
to start creating content.