This guide covers a complete, production-ready Node.js deployment pattern: PM2 keeping your app running reliably, and Nginx as a reverse proxy handling public traffic and SSL.
Prerequisites
- Node.js installed (see How to Install Node.js on Ubuntu & Debian)
- Nginx installed
- A Node.js application ready to deploy
Step 1 — Transfer Your Application
rsync -avz ./myapp/ deploy@YOUR_SERVER_IP:/var/www/myapp/
Step 2 — Install Dependencies on the Server
cd /var/www/myapp
npm install --production
Step 3 — Install PM2 Globally
npm install -g pm2
Step 4 — Start Your Application with PM2
pm2 start app.js --name myapp
For apps with an npm start script:
pm2 start npm --name myapp -- start
Step 5 — Configure PM2 to Start on Boot
pm2 startup
pm2 save
pm2 startup generates and displays a command to run — copy and execute it exactly as shown to register PM2 with systemd.
Step 6 — Configure Nginx as a Reverse Proxy
sudo nano /etc/nginx/sites-available/myapp
server {
listen 80;
server_name myapp.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;
}
}
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Step 7 — Add HTTPS
sudo certbot --nginx -d myapp.example.com
Managing Your Application
pm2 list
pm2 logs myapp
pm2 restart myapp
pm2 reload myapp
pm2 reload performs a zero-downtime restart for apps running in cluster mode.
Running Multiple Instances (Cluster Mode)
pm2 start app.js -i max --name myapp
-i max automatically scales instances to match available CPU cores, distributing load across them.
Deploying Updates
cd /var/www/myapp
git pull
npm install --production
pm2 reload myapp
Common Errors
502 Bad Gateway — the app isn't running or crashed; check pm2 logs myapp.
App doesn't survive a reboot — confirm pm2 startup and pm2 save were both run correctly.
WebSocket connections fail — verify the Upgrade/Connection headers are present in the Nginx config.
Best Practices
- Bind your Node.js app to
127.0.0.1, not0.0.0.0, so it's only reachable through Nginx - Use PM2's cluster mode for CPU-bound applications to utilize multiple cores
- Always run
pm2 saveafter any change to which apps PM2 manages
Related Articles
- How to Install Node.js on Ubuntu & Debian (with NVM)
- Nginx as a Reverse Proxy for Node.js/Docker Apps
- How to Install Let's Encrypt SSL with Certbot (Nginx & Apache)
