Nginx + PM2 VPS Deployment Guide
🚀 Nginx + PM2 VPS Deployment Guide
This document is created based on your real production issue where Nginx kept routing traffic to port 3001 instead of the intended port (3002 / 3050). Follow this guide strictly to never face this issue again during VPS deployments.
1️⃣ How Nginx Decides Which App Gets Traffic (VERY IMPORTANT)
Nginx follows this exact order:
Match
server_nameexactlyIf no match, use the first server block listening on that port
If
default_serveris defined → use it
⚠️ Your issue happened because:
A domain/subdomain was missing or mismatched
Nginx fell back to the first server block → which was running on 3001
2️⃣ Golden Rule (Never Break This)
ONE DOMAIN / SUBDOMAIN = ONE SERVER BLOCK
❌ Wrong:
Multiple configs with same
server_nameMissing
wwwversionOld configs still enabled
✅ Correct:
server_name example.com www.example.com;3️⃣ Mandatory Folder Hygiene
Always check enabled sites
ls -l /etc/nginx/sites-enabled/❌ Delete unused or old configs:
sudo rm /etc/nginx/sites-enabled/old-fileOnly keep active domains.
4️⃣ Use a Catch-All Default Server (VERY IMPORTANT)
This prevents traffic from going to random apps like 3001.
server {
listen 80 default_server;
server_name _;
return 404 "Unknown domain";
}✅ This ensures:
Wrong domain → 404
No accidental routing
5️⃣ Standard Production Nginx Template (Use This Always)
server {
listen 80;
server_name api.example.com;
location = / {
return 200 '{"status":"API running"}';
add_header Content-Type application/json;
}
location / {
proxy_pass http://localhost:3050;
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;
}
}6️⃣ Always Test Before Reloading
sudo nginx -t
sudo systemctl reload nginx❌ Never skip this
7️⃣ Debugging Checklist (When Something Feels Wrong)
🔍 See where Nginx is routing
sudo nginx -T | grep -E 'server_name|proxy_pass'🔍 Test without browser cache
curl -I http://yourdomain.com🔍 Check PM2 ports
pm2 list8️⃣ PM2 Best Practices
Stop unused apps
pm2 stop <id>
pm2 delete <id>Name apps clearly
pm2 start dist/main.js --name api-patalganga -- --port=30509️⃣ Root Path (/) Handling
If your backend has no / route, users will see 404.
Option A: Add root route in backend (recommended)
Option B: Redirect in Nginx
location = / {
return 301 /api;
}🔟 Final Deployment Checklist (Print This)
✅ DNS points to correct VPS IP ✅ Correct server_name (with www) ✅ No duplicate configs ✅ Catch-all default server exists ✅ PM2 ports verified ✅ nginx -t passed ✅ curl -I tested
🧠 Root Cause of Your Issue (Summary)
Missing or mismatched
server_nameNo default server
Old PM2 app still running
Nginx fallback to first server block (3001)
✅ If You Follow This Doc
You will never face:
Traffic going to wrong port
Random app loading
Confusing PM2 behavior
📌 Recommended next step:Create a /etc/nginx/snippets/standard-proxy.conf and reuse it for all apps.
If you want, I can also convert this into a PDF / Notion / README.md for your DevOps workflow.
Discussion (0)
Please sign in to join the discussion.
Sign In to CommentHave any query???
Get in touch and let's discuss your query.