DeployWise
HomeGuidesNext.js + PM2 + Nginx
PM2NginxNext.jsProductionSSL

Next.js + PM2 + Nginx: Production Setup on a VPS

Learn how PM2 and Nginx work together to run your Next.js app in production — with auto-restart on crash, zero-downtime reloads, and free SSL. And how DeployWise automates all of it in one click.

10 min read
Updated 2026

The production stack explained

Running Next.js in production on a VPS involves three layers:

Next.js
Application
Your app runs as a Node.js process listening on a port (default: 3000). It serves both SSR pages and API routes.
PM2
Process Manager
PM2 keeps your Node.js process alive. If the app crashes, PM2 restarts it automatically. It also handles graceful reloads and log management.
Nginx
Reverse Proxy
Nginx sits in front of PM2/Node.js, accepts requests on port 80/443, handles SSL termination, and proxies traffic to port 3000.

The request flow is: Client → Nginx (443) → PM2 → Next.js (3000)

What is PM2 and why use it?

PM2 is the de facto standard process manager for Node.js apps in production. Without it, if your Next.js app crashes (unhandled exception, memory leak, etc.) it stays down until you manually restart it — potentially losing hours of uptime.

Auto-restart on crash — app recovers in milliseconds
Startup scripts — PM2 can start your app on server reboot (pm2 startup)
Zero-downtime reloads — pm2 reload restarts without dropping connections
Log management — aggregates stdout/stderr with timestamps
Cluster mode — fork multiple instances across CPU cores
Memory limit — restart if app exceeds a configured memory threshold

Manual setup (what DeployWise automates)

Here's what the manual setup looks like — so you understand what's happening under the hood when DeployWise deploys your app:

1. Install Node.js and PM2

bash
# Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc

# Install Node.js LTS
nvm install --lts
nvm use --lts

# Install PM2 globally
npm install -g pm2

2. Build and start your Next.js app

bash
# Clone your repo
git clone https://github.com/you/my-app.git /var/www/my-app
cd /var/www/my-app

# Install dependencies
npm ci

# Build for production
npm run build

# Start with PM2
pm2 start npm --name "my-app" -- start
pm2 save

# Enable auto-start on reboot
pm2 startup

3. Install and configure Nginx

bash
# Install Nginx
sudo apt update && sudo apt install -y nginx

# Create site config
sudo nano /etc/nginx/sites-available/my-app
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;
    }
}
bash
# Enable the site
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

4. Install free SSL with Certbot

bash
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx

# Get SSL certificate (auto-configures Nginx)
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

# Auto-renewal is set up automatically by Certbot

Useful PM2 commands

bash
# View all running processes
pm2 list

# Stream live logs
pm2 logs my-app

# Restart (hard restart)
pm2 restart my-app

# Reload (zero-downtime)
pm2 reload my-app

# Stop process
pm2 stop my-app

# Delete process
pm2 delete my-app

# Monitor CPU/memory usage
pm2 monit

Useful Nginx commands

bash
# Test configuration syntax
sudo nginx -t

# Reload config (no downtime)
sudo systemctl reload nginx

# Restart Nginx
sudo systemctl restart nginx

# View error log
sudo tail -f /var/log/nginx/error.log

# View access log
sudo tail -f /var/log/nginx/access.log

How DeployWise automates all of this

Every step above — NVM installation, PM2 setup, Nginx configuration, SSL — is executed automatically by DeployWise when you click Deploy. You never touch the terminal.

On subsequent deploys, DeployWise runs a zero-downtime update:

1.Pull latest code from GitHub
2.Install new/changed dependencies
3.Run the build command
4.Reload the PM2 process (zero-downtime)
5.Nginx config is updated if domain/port changed

Common production gotchas

next.config.js output: 'export' breaks API routes

Static export doesn't support API routes or SSR. Use output: 'standalone' for SSR apps on VPS.

PM2 process shows 'errored' after deploy

Run pm2 logs my-app to see the error. Most common cause: missing environment variables. Set them in a .env file or via PM2 ecosystem.config.js.

Nginx returns 502 Bad Gateway

Your Node.js process isn't running or isn't listening on the expected port. Check pm2 status and ensure the proxy_pass port matches your app's PORT env variable.

App works but styles/images are missing

The Next.js build may have used an incorrect NEXT_PUBLIC_URL. Ensure environment variables are set before the build step, not just at runtime.

Skip the manual setup entirely

DeployWise handles Node.js, PM2, Nginx and SSL automatically — from a clean dashboard. No terminal required.

Try DeployWise Free

Related guides