← Back to Blog

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:

  1. Match server_name exactly

  2. If no match, use the first server block listening on that port

  3. If default_server is 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_name

  • Missing www version

  • Old 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-file

Only 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 list

8️⃣ PM2 Best Practices

  • Stop unused apps

pm2 stop <id>
pm2 delete <id>
  • Name apps clearly

pm2 start dist/main.js --name api-patalganga -- --port=3050

9️⃣ 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_name

  • No 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.

— End of the article —

Discussion (0)

Please sign in to join the discussion.

Sign In to Comment

Have any query???

Get in touch and let's discuss your query.

Send a message