PayloadCMS is a powerful, TypeScript-first headless CMS that offers an excellent developer experience with a beautiful admin interface. In this comprehensive guide, we’ll walk through setting up PayloadCMS on a Ramnode VPS using nginx as our web server and MySQL as our database.

Prerequisites

Before we begin, you’ll need:

  • A Ramnode VPS running Ubuntu 22.04 LTS (recommended)
  • Root or sudo access to your server
  • A domain name pointed to your server’s IP address
  • Basic familiarity with command line operations

Initial Server Setup

First, let’s update our system and install essential packages:

sudo apt update && sudo apt upgrade -y
sudo apt install curl wget git unzip software-properties-common -y

Install Node.js and npm

PayloadCMS requires Node.js 18 or higher. We’ll use NodeSource repository for the latest LTS version:

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install nodejs -y

Verify the installation:

node --version
npm --version

Install and Configure MySQL

Install MySQL server:

sudo apt install mysql-server -y

Secure your MySQL installation:

sudo mysql_secure_installation

Follow the prompts to:

  • Set a strong root password
  • Remove anonymous users
  • Disallow root login remotely
  • Remove test database
  • Reload privilege tables

Create Database and User

Log into MySQL and create a database for PayloadCMS:

sudo mysql -u root -p

Execute the following SQL commands:

CREATE DATABASE payloadcms_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'payloadcms_user'@'localhost' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON payloadcms_db.* TO 'payloadcms_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

Install and Configure Nginx

Install nginx:

sudo apt install nginx -y

Start and enable nginx:

sudo systemctl start nginx
sudo systemctl enable nginx

Create a new nginx configuration file for your PayloadCMS site:

sudo nano /etc/nginx/sites-available/payloadcms

Add the following configuration (replace yourdomain.com with your actual domain):

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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_cache_bypass $http_upgrade;
        proxy_read_timeout 300s;
        proxy_connect_timeout 75s;
    }

    # Optional: Serve static files directly with nginx
    location /media {
        alias /var/www/payloadcms/media;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

Enable the site and remove the default configuration:

sudo ln -s /etc/nginx/sites-available/payloadcms /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default

Test the nginx configuration:

sudo nginx -t

If the test passes, reload nginx:

sudo systemctl reload nginx

Install PayloadCMS

Create a directory for your PayloadCMS project:

sudo mkdir -p /var/www/payloadcms
sudo chown $USER:$USER /var/www/payloadcms
cd /var/www/payloadcms

Create a new PayloadCMS project:

npx create-payload-app@latest .

During the setup process, choose:

  • TypeScript: Yes (recommended)
  • Database: Use MySQL
  • Database connection: Use the connection string format:
    mysql://payloadcms_user:your_strong_password@localhost:3306/payloadcms_db
    

Install Additional Dependencies

Install PM2 for process management:

sudo npm install -g pm2

Configure Environment Variables

Create a .env file in your project root:

nano .env

Add the following environment variables:

DATABASE_URI=mysql://payloadcms_user:your_strong_password@localhost:3306/payloadcms_db
PAYLOAD_SECRET=your_very_secure_secret_key_here
NODE_ENV=production
PORT=3000

Generate a secure secret key:

openssl rand -base64 32

Use this generated key for your PAYLOAD_SECRET.

Build and Deploy PayloadCMS

Install dependencies and build the project:

npm install
npm run build

Create a PM2 ecosystem file for better process management:

nano ecosystem.config.js

Add the following configuration:

module.exports = {
  apps: [{
    name: 'payloadcms',
    script: 'dist/server.js',
    instances: 1,
    autorestart: true,
    watch: false,
    max_memory_restart: '1G',
    env: {
      NODE_ENV: 'production',
      PORT: 3000
    }
  }]
};

Start the application with PM2:

pm2 start ecosystem.config.js
pm2 save
pm2 startup

Follow the instructions provided by the pm2 startup command to enable PM2 to start on system boot.

Set Up SSL with Let’s Encrypt

Install Certbot:

sudo apt install snapd -y
sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Obtain an SSL certificate:

sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot will automatically update your nginx configuration to include SSL settings.

Configure Firewall

Set up a basic firewall using UFW:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Final Configuration and Testing

Update File Permissions

Ensure proper permissions for media uploads:

sudo mkdir -p /var/www/payloadcms/media
sudo chown -R $USER:www-data /var/www/payloadcms
sudo chmod -R 755 /var/www/payloadcms

Test Your Installation

Visit your domain in a web browser. You should see the PayloadCMS admin interface. Create your first admin user and start building your content structure.

Performance Optimization Tips

1. Configure nginx Caching

Add caching directives to your nginx configuration:

location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

2. Enable Gzip Compression

Add to your nginx configuration:

gzip on;
gzip_vary on;
gzip_min_length 10240;
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;

3. MySQL Optimization

Edit your MySQL configuration:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

Add optimizations based on your server resources:

innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2
query_cache_size = 32M

Restart MySQL after making changes:

sudo systemctl restart mysql

Maintenance and Monitoring

Regular Updates

Keep your system and PayloadCMS updated:

# System updates
sudo apt update && sudo apt upgrade -y

# PayloadCMS updates
cd /var/www/payloadcms
npm update
npm run build
pm2 restart payloadcms

Backup Strategy

Set up automated backups for your database:

# Create backup script
sudo nano /usr/local/bin/backup-payloadcms.sh

Add the following script:

#!/bin/bash
BACKUP_DIR="/var/backups/payloadcms"
DATE=$(date +"%Y%m%d_%H%M%S")
DB_NAME="payloadcms_db"
DB_USER="payloadcms_user"
DB_PASS="your_strong_password"

mkdir -p $BACKUP_DIR

# Database backup
mysqldump -u$DB_USER -p$DB_PASS $DB_NAME > $BACKUP_DIR/db_backup_$DATE.sql

# Media files backup
tar -czf $BACKUP_DIR/media_backup_$DATE.tar.gz /var/www/payloadcms/media

# Keep only last 7 days of backups
find $BACKUP_DIR -name "*.sql" -mtime +7 -delete
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete

Make it executable and add to crontab:

sudo chmod +x /usr/local/bin/backup-payloadcms.sh
sudo crontab -e

Add this line for daily backups at 2 AM:

0 2 * * * /usr/local/bin/backup-payloadcms.sh

Troubleshooting Common Issues

PayloadCMS Won’t Start

Check the PM2 logs:

pm2 logs payloadcms

Common issues include:

  • Database connection problems
  • Missing environment variables
  • Port conflicts

Database Connection Errors

Verify MySQL is running and accessible:

sudo systemctl status mysql
mysql -u payloadcms_user -p -e "SELECT 1;"

nginx Configuration Issues

Test and reload nginx configuration:

sudo nginx -t
sudo systemctl reload nginx

Conclusion

You now have a fully functional PayloadCMS installation running on your Ramnode VPS with nginx and MySQL. This setup provides a solid foundation for building modern web applications with a powerful headless CMS.

The configuration we’ve implemented includes SSL encryption, process management, and basic security measures. Remember to regularly update your system, monitor performance, and maintain backups to ensure your PayloadCMS installation remains secure and reliable.

PayloadCMS offers extensive customization options through its configuration files, custom fields, and hooks system. Explore the official documentation to unlock the full potential of your new headless CMS setup.