Appearance
Deploy to VPS
Deploy Portfolio CMS to a Virtual Private Server for full control over your infrastructure.
Prerequisites
- VPS with Ubuntu 22.04+ (DigitalOcean, Linode, Vultr, etc.)
- Domain name pointed to your server
- SSH access to your server
- Basic Linux command line knowledge
Recommended Specs
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2 vCPU |
| RAM | 2 GB | 4 GB |
| Storage | 20 GB SSD | 40 GB SSD |
| OS | Ubuntu 22.04 | Ubuntu 22.04 |
Server Setup
Step 1: Initial Server Configuration
bash
# Connect to your server
ssh root@your_server_ip
# Update system
apt update && apt upgrade -y
# Create deploy user
adduser deploy
usermod -aG sudo deploy
# Setup SSH for deploy user
mkdir -p /home/deploy/.ssh
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys
# Switch to deploy user
su - deployStep 2: Install Node.js
bash
# Install Node.js 20 via NodeSource
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# Verify installation
node --version
npm --version
# Install pnpm
sudo npm install -g pnpmStep 3: Install PostgreSQL
bash
# Install PostgreSQL
sudo apt install -y postgresql postgresql-contrib
# Create database and user
sudo -u postgres psql
# In PostgreSQL:
CREATE USER portfolio WITH PASSWORD 'your_secure_password';
CREATE DATABASE portfoliocms OWNER portfolio;
GRANT ALL PRIVILEGES ON DATABASE portfoliocms TO portfolio;
\qStep 4: Install Nginx
bash
# Install Nginx
sudo apt install -y nginx
# Start and enable
sudo systemctl start nginx
sudo systemctl enable nginx
# Check status
sudo systemctl status nginxStep 5: Install PM2
bash
# Install PM2 globally
sudo npm install -g pm2
# Setup PM2 startup
pm2 startup systemd
# Follow the instructions it outputsApplication Setup
Step 1: Clone Repository
bash
# Create app directory
sudo mkdir -p /var/www/portfolio
sudo chown deploy:deploy /var/www/portfolio
# Clone your repository
cd /var/www/portfolio
git clone https://github.com/yourusername/portfoliocms.git .Step 2: Install Dependencies
bash
# Install dependencies
pnpm installStep 3: Configure Environment
bash
# Create .env file
nano .envAdd your environment variables:
env
DATABASE_URL=postgresql://portfolio:your_secure_password@localhost:5432/portfoliocms
PAYLOAD_SECRET=your-super-secret-key-at-least-32-characters
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
S3_BUCKET=your-bucket-name
S3_ACCOUNT_ID=your-account-id
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET=your-secret-key
RESEND_API_KEY=re_...
RESEND_FROM_EMAIL=...
RESEND_FROM_NAME=...
NODE_ENV=production
PORT=3000Step 4: Build Application
bash
# Generate types
pnpm generate:types
# Run migrations
pnpm payload migrate
# Build
pnpm buildStep 5: Start with PM2
bash
# Start the application
pm2 start pnpm --name "portfolio" -- start
# Save PM2 configuration
pm2 save
# View logs
pm2 logs portfolioNginx Configuration
Create Nginx Config
bash
sudo nano /etc/nginx/sites-available/portfolioAdd configuration:
nginx
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;
}
# Increase max body size for uploads
client_max_body_size 10M;
}Enable Configuration
bash
# Enable site
sudo ln -s /etc/nginx/sites-available/portfolio /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginxSSL Certificate
Install Certbot
bash
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Follow the promptsAuto-Renewal
Certbot sets up auto-renewal automatically. Test it:
bash
sudo certbot renew --dry-runDeployment Updates
Manual Updates
bash
cd /var/www/portfolio
# Pull latest changes
git pull origin main
# Install new dependencies
pnpm install
# Run migrations (if any)
pnpm payload migrate
# Rebuild
pnpm build
# Restart PM2
pm2 restart portfolioAutomated Deployment Script
Create deploy.sh:
bash
#!/bin/bash
set -e
cd /var/www/portfolio
echo "Pulling latest changes..."
git pull origin main
echo "Installing dependencies..."
pnpm install
echo "Running migrations..."
pnpm payload migrate
echo "Building application..."
pnpm build
echo "Restarting PM2..."
pm2 restart portfolio
echo "Deployment complete!"Make executable:
bash
chmod +x deploy.shGitHub Actions Deployment
Create .github/workflows/deploy.yml:
yaml
name: Deploy to VPS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/portfolio
./deploy.shMonitoring
PM2 Monitoring
bash
# View process list
pm2 list
# View logs
pm2 logs portfolio
# Monitor resources
pm2 monit
# View detailed info
pm2 show portfolioSystem Monitoring
bash
# Install htop
sudo apt install htop
# Monitor system
htopLog Rotation
bash
# Install PM2 log rotation
pm2 install pm2-logrotate
# Configure
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7Security
Firewall Setup
bash
# Enable UFW
sudo ufw enable
# Allow SSH
sudo ufw allow ssh
# Allow HTTP/HTTPS
sudo ufw allow 'Nginx Full'
# Check status
sudo ufw statusFail2ban
bash
# Install fail2ban
sudo apt install -y fail2ban
# Start and enable
sudo systemctl start fail2ban
sudo systemctl enable fail2banSecurity Headers
Add to Nginx config:
nginx
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;Troubleshooting
Application Won't Start
bash
# Check PM2 logs
pm2 logs portfolio --lines 100
# Check for port conflicts
sudo lsof -i :3000
# Verify environment variables
cat .envDatabase Connection Issues
bash
# Check PostgreSQL status
sudo systemctl status postgresql
# Test connection
psql -U portfolio -d portfoliocms -h localhostNginx Errors
bash
# Check Nginx error log
sudo tail -f /var/log/nginx/error.log
# Test configuration
sudo nginx -tSSL Certificate Issues
bash
# Check certificate status
sudo certbot certificates
# Force renewal
sudo certbot renew --force-renewalBackup Strategy
Database Backup
bash
# Create backup
pg_dump -U portfolio portfoliocms > backup_$(date +%Y%m%d).sql
# Automate with cron
crontab -e
# Add: 0 2 * * * pg_dump -U portfolio portfoliocms > /backups/db_$(date +\%Y\%m\%d).sqlApplication Backup
bash
# Backup entire app
tar -czvf portfolio_backup.tar.gz /var/www/portfolio