Here’s a step-by-step walkthrough for deploying your Next.js App Router application on an Ubuntu VPS, managed by PM2 and fronted by Nginx:
1. VPS Prep & SSH Access
Provision your VPS, e.g. Ubuntu 22.04 LTS on DigitalOcean, AWS EC2, etc.
Open SSH port (22) in your provider’s dashboard (or change to a custom port if you prefer).
SSH in:
ssh root@your_server_ip
2. System Update & Node.js Installation
# Update packages
apt update && apt upgrade -y
# Install curl, build tools
apt install -y curl build-essential
# Install Node.js (LTS) via NodeSource
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
apt install -y nodejs
Verify versions
node -v # e.g. v18.x npm -v # e.g. 9.x
3. Create Deployment User (Optional but Recommended)
adduser deployer
usermod -aG sudo deployer
- Then exit root and SSH in as
deployer
.
4. Clone & Install Your Next.js App
Navigate to your preferred directory, e.g.:
mkdir -p ~/apps/nextjs-app && cd ~/apps/nextjs-app
Clone your repo (or pull latest):
git clone https://github.com/yourusername/your-nextjs-repo.git .
Install dependencies & build:
npm ci # or yarn install --frozen-lockfile npm run build
5. Configure PM2
Install PM2 globally:
npm install -g pm2
Start Next.js in production:
pm2 start npm --name "nextjs-app" -- run start
Generate PM2 ecosystem file (optional, for env vars & clustering):
pm2 ecosystem
Edit
ecosystem.config.js
:module.exports = { apps: [ { name: "nextjs-app", script: "npm", args: "run start", cwd: "/home/deployer/apps/nextjs-app", instances: "max", exec_mode: "cluster", env: { NODE_ENV: "production", // Add other env vars here } } ] };
Start via ecosystem:
pm2 start ecosystem.config.js
Persist PM2 across reboots:
pm2 save pm2 startup systemd # Follow the printed instructions to enable the service
6. Install & Configure Nginx
Install Nginx:
sudo apt install -y nginx
Create Nginx site file at
/etc/nginx/sites-available/nextjs-app
:server { listen 80; server_name your_domain_or_ip; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } # Optional: serve static assets directly location /_next/static/ { alias /home/deployer/apps/nextjs-app/.next/static/; access_log off; expires max; } }
Enable & restart:
ln -s /etc/nginx/sites-available/nextjs-app /etc/nginx/sites-enabled/ nginx -t systemctl reload nginx
7. (Optional) Secure with Let’s Encrypt SSL
Install Certbot:
apt install -y certbot python3-certbot-nginx
Obtain & configure cert:
certbot --nginx -d your_domain.com -d www.your_domain.com
Auto-renewal (installed by default as a cron job).
8. Monitoring & Logs
View PM2 status:
pm2 status
Inspect logs:
pm2 logs nextjs-app --lines 100
Restart on code changes (dev only):
pm2 start npm --name "nextjs-dev" --watch -- run dev
Recap of Key Commands
# System & Node
apt update && apt upgrade -y
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
apt install -y nodejs nginx
# Clone & build
git clone repo .
npm ci && npm run build
# PM2
npm install -g pm2
pm2 start npm --name "nextjs-app" -- run start
pm2 save && pm2 startup systemd
# Nginx
# create /etc/nginx/sites-available/nextjs-app (as above)
ln -s /etc/nginx/sites-available/nextjs-app /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
# SSL (optional)
apt install -y certbot python3-certbot-nginx
certbot --nginx -d your_domain.com
With this setup, your Next.js application is running under PM2, automatically restarts on crashes or reboots, and is served securely (if using SSL) behind Nginx.