TL;DR
Step-by-step guide to deploying a Next.js application on a DigitalOcean Droplet with PM2, nginx, and SSL.
Key facts
- Topology
- Next.js on DigitalOcean
TL;DR
This guide walks through deploying a Next.js application to a DigitalOcean Droplet with Node.js, PM2, nginx, and SSL — a production-ready setup without vendor lock-in.
Prerequisites
- A DigitalOcean account and a Droplet running Ubuntu 24.04 (minimum 2 GB RAM for Next.js builds)
- A domain name with DNS pointing to the Droplet's IP
- Your Next.js project in a Git repository
Step 1 — Droplet setup
SSH into your Droplet and prepare the system:
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git build-essential
Install Node.js 22 LTS:
curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
Step 2 — Deploy the application
sudo mkdir -p /var/www/nextapp
sudo chown $USER:$USER /var/www/nextapp
cd /var/www/nextapp
git clone git@github.com:yourorg/nextapp.git .
npm ci
Create your .env.production with the correct NEXT_PUBLIC_* variables, then build:
npm run build
Step 3 — Process management with PM2
sudo npm install -g pm2
pm2 start npm --name "nextapp" -- start
pm2 startup systemd
pm2 save
For standalone output mode (recommended for production), update next.config.js:
module.exports = { output: 'standalone' };
Then run the standalone server:
pm2 start .next/standalone/server.js --name "nextapp" -i max
Step 4 — nginx reverse proxy
sudo apt install -y nginx
server {
listen 80;
server_name example.com;
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_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;
}
location /_next/static {
alias /var/www/nextapp/.next/static;
expires 365d;
access_log off;
}
}
sudo ln -s /etc/nginx/sites-available/nextapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Step 5 — SSL with Let's Encrypt
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com
Certbot's auto-renewal timer is enabled by default. Verify with:
sudo systemctl status certbot.timer
With Reflex
Connect your Droplet to Reflex for continuous monitoring and safe deployments. Reflex handles zero-downtime PM2 reloads, tracks Next.js build performance, monitors nginx error rates, and can auto-fix common issues like stale builds or memory spikes — all from a single dashboard. See How it works.