Article

Step-by-Step: Deploying Next.js with PM2 + Nginx on AWS EC2

·6 min read min read·👁 66
Dharmendra Singh Yadav

Dharmendra Singh Yadav

Founder, Dharmsy Innovations

Step-by-Step: Deploying Next.js with PM2 + Nginx on AWS EC2


What you’ll build

  1. EC2 (Ubuntu) runs your Next.js server (next start) managed by PM2.
  2. Nginx sits in front as a reverse proxy on :80/:443, terminates TLS, serves gzip/brotli, and forwards to Node on :3000.
  3. Let’s Encrypt issues a free certificate (auto-renew).
  4. Zero-downtime deploys with pm2 reload.

Prerequisites

  1. Domain pointing to your EC2 public IP (e.g., A record for app.example.com).
  2. An EC2 t2.micro / t3.small+ (Arm or x86 is fine).
  3. Security Group: allow 22, 80, 443 (lock 22 to your IP if possible).
  4. Your app builds locally with Node 18+ (Next.js requires modern Node).

1) Launch & harden the instance

SSH in:

ssh -i ~/.ssh/your-key.pem ubuntu@your-ec2-public-ip

Update & base tools:

sudo apt update && sudo apt -y upgrade
sudo apt -y install curl git ufw

Firewall (optional but recommended):

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable

2) Install Node.js (nvm) & PM2

# nvm
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.nvm/nvm.sh
nvm install --lts
node -v && npm -v

# PM2 (global)
npm i -g pm2
pm2 -v

If you use pnpm or yarn, also install Corepack:

corepack enable

3) Fetch your Next.js app & set env

Put your code in /var/www/app (or /home/ubuntu/app if you prefer):

sudo mkdir -p /var/www/app
sudo chown -R ubuntu:ubuntu /var/www
cd /var/www/app

git clone https://github.com/your-org/your-nextjs-repo.git .
# or scp your build artifacts

Environment variables:

cp .env.example .env # or create .env.production
nano .env # set NEXT_PUBLIC_* and server secrets

Install deps & build:

npm ci # or npm i / pnpm i / yarn
npm run build

Sanity test (temporarily):

npm run start -- -p 3000
# open http://YOUR_EC2_IP:3000 (then Ctrl+C)

4) Run with PM2 (and auto-restart)

Option A: simple start

pm2 start npm --name "next-app" -- start -- -p 3000

Option B: ecosystem file (recommended)

Create /var/www/app/ecosystem.config.js:

module.exports = {
apps: [
{
name: "next-app",
cwd: "/var/www/app",
script: "npm",
args: "start -- -p 3000",
env: {
NODE_ENV: "production",
PORT: "3000"
},
instances: 1, // set to "max" for cluster if purely stateless
exec_mode: "fork", // "cluster" is OK for Next.js pages/API; avoid for dev SSR with websockets unless tested
autorestart: true,
watch: false,
max_memory_restart: "512M",
}
]
}

Start & save:

pm2 start ecosystem.config.js
pm2 status
pm2 save
pm2 startup systemd -u ubuntu --hp /home/ubuntu
# follow the printed command, then:
sudo systemctl status pm2-ubuntu

Logs:

pm2 logs next-app

Optional log rotation:

pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 14

5) Install & configure Nginx

sudo apt -y install nginx
sudo systemctl enable --now nginx

Create a site file at /etc/nginx/sites-available/next-app:

server {
listen 80;
listen [::]:80;
server_name app.example.com;

# Optional: serve a quick health endpoint
location /healthz { return 200 "ok\n"; add_header Content-Type text/plain; }

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-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 60s;
}

# Static compression hints (Next already serves optimized assets)
gzip on;
gzip_types text/plain text/css application/json application/javascript application/xml+rss image/svg+xml;
}

Enable and test:

sudo ln -s /etc/nginx/sites-available/next-app /etc/nginx/sites-enabled/next-app
sudo nginx -t
sudo systemctl reload nginx

Point your DNS (app.example.com) to the EC2 public IP and verify http://app.example.com routes to your app via Nginx.

6) Add HTTPS (Let’s Encrypt)

sudo apt -y install certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com --redirect -m you@domain.com --agree-tos -n
sudo systemctl status certbot.timer # auto-renew

Nginx will be updated to HTTP/2 + 301 redirect to https://.

7) Zero-downtime deploys

Create a small deploy script /var/www/app/deploy.sh:

#!/usr/bin/env bash
set -euo pipefail
APP_DIR=/var/www/app

cd $APP_DIR
git fetch --all
git reset --hard origin/main

npm ci
npm run build

# Atomically reload without dropping connections
pm2 reload next-app

Make it executable:

chmod +x /var/www/app/deploy.sh

Deploy with:

/var/www/app/deploy.sh
If you serve APIs with websockets, validate pm2 reload doesn’t drop sessions; fall back to gracefulReload patterns if needed.

8) Multiple apps or staging

Add another PM2 app on a different port (e.g., 3001) and create a second Nginx server block for staging.example.com that proxies to 127.0.0.1:3001. Certbot can issue a cert for the new host as well.

9) Health checks & monitoring

  1. Health route (already included): GET /healthz → 200 ok.
  2. PM2 tools:
pm2 monit
pm2 list
pm2 logs
  1. Ship logs to CloudWatch/ELK (optional).
  2. Alarms: CPU > 70%, Memory > 75%, Nginx 5xx rate.

10) Common issues & fixes

502 Bad Gateway (Nginx)

  1. PM2 app not running or wrong port; check pm2 status, pm2 logs, and proxy_pass target.

Build fails on server

  1. Node version mismatch. Ensure nvm use --lts and match local Node.
  2. Missing system libs for sharp/next-image: sudo apt -y install build-essential python3 make g++ then rebuild.

TLS works but mixed content

  1. Use absolute HTTPS URLs for any external assets; set X-Forwarded-Proto (we already do).

Huge images slow pages

  1. Use <Image /> with next/image and set images.domains in next.config.js.

11) Hardening & operations

  1. Disable password SSH; use keys only (/etc/ssh/sshd_config).
  2. Regular security updates: unattended-upgrades.
  3. Backups: AMIs + S3 backups for .env (encrypted) and any persistent uploads (ideally S3 only).
  4. If you must allow uploads: never store user files on the EC2 root; always S3 + CloudFront.

12) Scaling beyond one EC2

  1. Vertical: bump to t3.medium/t3.large.
  2. Horizontal: bake an AMI, put two+ instances in an Auto Scaling Group behind an Application Load Balancer (ALB). Terminate TLS at ALB; keep Nginx if you still want per-node caching/routing, or remove Nginx and point ALB → Node directly.
  3. Global: front with CloudFront (static caching) and regional ALBs for dynamic content.

Quick TL;DR (commands)

# On fresh Ubuntu EC2
sudo apt update && sudo apt -y upgrade
sudo apt -y install curl git nginx ufw
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.nvm/nvm.sh && nvm install --lts
npm i -g pm2

sudo mkdir -p /var/www/app && sudo chown -R ubuntu:ubuntu /var/www
cd /var/www/app && git clone https://github.com/your/repo.git . && npm ci && npm run build
pm2 start npm --name "next-app" -- start -- -p 3000
pm2 save && pm2 startup systemd -u ubuntu --hp /home/ubuntu

# Nginx reverse proxy & HTTPS
# (create site file as shown), then:
sudo nginx -t && sudo systemctl reload nginx
sudo apt -y install certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com --redirect -m you@domain.com --agree-tos -n


Frequently Asked Questions

What you’ll build?+

EC2 (Ubuntu) runs your Next.js server (next start) managed by PM2 . Nginx sits in front as a reverse proxy on :80/:443 , terminates TLS , serves gzip/brotli, and forwards to Node on :3000 . Let’s Encrypt issues a free certificate (auto-renew).

What is Prerequisites?+

Domain pointing to your EC2 public IP (e.g., A record for app.example.com). An EC2 t2.micro / t3.small+ (Arm or x86 is fine). Security Group: allow 22 , 80 , 443 (lock 22 to your IP if possible).

Fetch your Next.js app & set env?+

Put your code in /var/www/app (or /home/ubuntu/app if you prefer):

Option B: ecosystem file (recommended)?+

Create /var/www/app/ecosystem.config.js:

What is Install & configure Nginx?+

Create a site file at /etc/nginx/sites-available/next-app:

How can Dharmsy help?+

Dharmsy builds production-grade web, mobile, and SaaS products. Share your requirements and we'll give you a clear, honest plan.

Work with Dharmsy Innovations

Turn Your SaaS or App Idea Into a Real Product — Faster & Affordable

Dharmsy Innovations helps founders and businesses turn ideas into production-ready products — from MVP and prototypes to scalable platforms in web, mobile, and AI.

No sales pressure — just honest guidance on cost, timeline & tech stack.