Deploy Node.js App: PM2 and Nginx Guide
Complete guide to deploying Node.js applications on a VPS. Node.js installation with nvm, PM2 process manager, Nginx reverse proxy, SSL certificate, and environment variables management.
Guide to deploying Next.js and React applications on a VPS. Node.js setup, Next.js build and start, PM2 process manager, Nginx reverse proxy, environment variables, and CI/CD basics.
Next.js is the most popular framework for modern React-based web applications. Running Next.js on your own VPS instead of platforms like Vercel offers cost savings, full control, and customization. This guide walks you through deploying a Next.js application to a VPS step by step.
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc
# Install LTS version
nvm install --lts
nvm use --lts
nvm alias default node
# Check versions
node --version
npm --version
# Create application directory
sudo mkdir -p /var/www/mynextapp
sudo chown $USER:$USER /var/www/mynextapp
# Clone the repo
cd /var/www
git clone https://github.com/user/mynextapp.git
cd mynextapp
# Install dependencies
npm install
# Create .env.local file
nano /var/www/mynextapp/.env.local
NODE_ENV=production
NEXT_PUBLIC_API_URL=https://api.example.com
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
NEXTAUTH_SECRET=your-secret-key-here
NEXTAUTH_URL=https://example.com
Variables prefixed with NEXT_PUBLIC_ are visible in the browser. Do not use this prefix for sensitive information (API keys, database passwords).
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
images: {
domains: ['example.com'],
},
async headers() {
return [
{
source: '/(.*)',
headers: [
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
],
},
];
},
};
module.exports = nextConfig;
# Create production build
npm run build
# Test the build
npm start
# Install PM2
npm install -g pm2
# Create ecosystem.config.js
nano /var/www/mynextapp/ecosystem.config.js
module.exports = {
apps: [
{
name: 'mynextapp',
script: '.next/standalone/server.js',
cwd: '/var/www/mynextapp',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000,
HOSTNAME: '0.0.0.0'
},
max_memory_restart: '1G',
autorestart: true,
watch: false
}
]
};
# Copy static files for standalone build
cp -r .next/static .next/standalone/.next/static
cp -r public .next/standalone/public
# Start the application
pm2 start ecosystem.config.js
# Auto-start on system boot
pm2 startup
pm2 save
# Check status
pm2 status
pm2 logs mynextapp
sudo apt install nginx -y
sudo nano /etc/nginx/sites-available/mynextapp
server {
listen 80;
server_name example.com www.example.com;
# Cache for Next.js static files
location /_next/static/ {
alias /var/www/mynextapp/.next/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
# Public folder
location /public/ {
alias /var/www/mynextapp/public/;
expires 30d;
add_header Cache-Control "public";
}
# Proxy to Next.js application
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 86400;
}
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;
client_max_body_size 50M;
}
sudo ln -s /etc/nginx/sites-available/mynextapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com
# .github/workflows/deploy.yml
name: Deploy to VPS
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/mynextapp
git pull origin main
npm install
npm run build
cp -r .next/static .next/standalone/.next/static
cp -r public .next/standalone/public
pm2 reload mynextapp
Add SERVER_HOST, SERVER_USER, and SSH_PRIVATE_KEY to GitHub Actions secrets. Add the SSH key to the server's authorized_keys file.
cd /var/www/mynextapp
git pull origin main
npm install
npm run build
cp -r .next/static .next/standalone/.next/static
cp -r public .next/standalone/public
pm2 reload mynextapp
pm2 logs mynextapp --lines 50
On REXE servers, at least 1 GB RAM is recommended for Next.js. Large applications may require 2 GB+ RAM during build. Use PM2 cluster mode to fully utilize CPU cores.
Your Next.js application is now running in a production environment on a VPS, managed by PM2, behind an Nginx reverse proxy, and secured with SSL. Set up a CI/CD pipeline with GitHub Actions to enable automatic deployment on every push.
Increase the Node.js memory limit during build: 'NODE_OPTIONS="--max-old-space-size=4096" npm run build'. Make sure your server has at least 2 GB RAM. Using standalone output in large projects reduces build time and memory usage.
Check the images.domains or images.remotePatterns setting in next.config.js. Verify that the /_next/image/ path in Nginx has the correct proxy configuration. Make sure the sharp package is installed: 'npm install sharp'.
Make sure the /api/ path in Nginx is proxied to the Next.js application. If using standalone build, check that server.js is running correctly. Review PM2 logs with 'pm2 logs mynextapp'.
Use standalone output (output: 'standalone' in next.config.js). Enable PM2 cluster mode. Add long-term cache for /_next/static/ in Nginx. Use next/image for image optimization. Identify large packages with bundle analyzer: 'npm install @next/bundle-analyzer'.
Complete guide to deploying Node.js applications on a VPS. Node.js installation with nvm, PM2 process manager, Nginx reverse proxy, SSL certificate, and environment variables management.
Complete guide to deploying PHP applications on a VPS. LAMP vs LEMP comparison, Apache/Nginx + PHP-FPM + MySQL setup, virtual host configuration, and PHP version management.
Guide to installing and optimizing WordPress on a VPS. LEMP stack setup, wp-config.php settings, file permissions, SSL, OPcache performance optimization, and security hardening.