Node.js + PM2: Node apps in production
Deploying Node.js in prod needs more than
node index.js. PM2 manages processes, cluster mode, auto-restarts, logs and monitoring. The standard solution for robust Node.js apps.
Introduction
Node.js alone is single-process. In prod you want:
- Multiple workers (cluster) to use all CPUs
- Auto-restart on crash
- Boot startup
- Centralized rotated logs
- Monitoring (CPU, RAM, latency)
- Zero-downtime reload
PM2 is the reference tool for all this.
Prerequisites
- Linux VPS Debian / Ubuntu
- Node.js 18+ installed
- Root access
- A Node.js app
Step 1: Install Node.js (NodeSource)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node --version
npm --version
Step 2: Install PM2
sudo npm install -g pm2
pm2 --version
Step 3: Start an app
cd /var/www/myapp
pm2 start index.js --name myapp
pm2 list
Step 4: Cluster mode
pm2 start index.js --name myapp -i max
-i max launches one instance per core. Or fixed:
pm2 start index.js --name myapp -i 4
PM2 launches 4 Node workers, round-robins requests.
Step 5: Boot startup
pm2 startup
PM2 displays a command to run as root, like:
sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u admin --hp /home/admin
Then save:
pm2 save
Step 6: Logs
pm2 logs
pm2 logs myapp
pm2 logs --lines 100
Files:
~/.pm2/logs/myapp-out.log
~/.pm2/logs/myapp-error.log
Auto-rotation:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 100M
pm2 set pm2-logrotate:retain 30
pm2 set pm2-logrotate:compress true
Step 7: ecosystem.config.js
module.exports = {
apps: [{
name: 'myapp',
script: './index.js',
instances: 'max',
exec_mode: 'cluster',
autorestart: true,
watch: false,
max_memory_restart: '500M',
env: {
NODE_ENV: 'production',
PORT: 3000,
DATABASE_URL: 'postgres://localhost/myapp'
},
error_file: '/var/log/pm2/myapp-error.log',
out_file: '/var/log/pm2/myapp-out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss Z'
}]
}
pm2 start ecosystem.config.js
pm2 save
Step 8: Zero-downtime reload
pm2 reload myapp
PM2 restarts workers one by one, no request lost. Much better than restart.
Deployment:
git pull
npm install
pm2 reload myapp
Step 9: Nginx reverse proxy
upstream myapp_backend {
server 127.0.0.1:3000;
keepalive 64;
}
server {
listen 443 ssl http2;
server_name myapp.com;
location / {
proxy_pass http://myapp_backend;
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;
}
}
Step 10: Real-time monitoring
pm2 monit
TUI: CPU, RAM, logs, per instance.
PM2 Plus (SaaS):
pm2 link <secret_key> <public_key>
Step 11: Environment variables
env_production: {
NODE_ENV: 'production',
PORT: 3000
},
env_staging: {
NODE_ENV: 'staging',
PORT: 3001
}
pm2 start ecosystem.config.js --env production
Step 12: Memory leaks
max_memory_restart: '500M'
PM2 auto-restarts worker over 500 MB. Temp workaround. To identify:
pm2 reload myapp --update-env
pm2 show myapp
Use Chrome DevTools heap snapshots or clinic.js.
Troubleshooting
App crashes in loop
pm2 logs myapp --err --lines 50
Often: missing module, port in use, missing env var.
"ENOMEM" out of memory
Worker exceeds max_memory_restart. Increase or fix leak.
Cluster mode doesn't work
App must be cluster-safe:
- No shared in-memory state (use Redis)
- WebSockets: use
socket.io-redisor sticky mode
PM2 doesn't start at boot
pm2 unstartup
pm2 startup
pm2 save
Logs too large
pm2 set pm2-logrotate:max_size 50M
pm2 flush
Useful commands
pm2 list
pm2 show myapp
pm2 logs myapp --lines 100
pm2 monit
pm2 restart myapp # hard restart
pm2 reload myapp # zero-downtime
pm2 stop myapp
pm2 delete myapp
pm2 save
pm2 resurrect
pm2 flush
pm2 startup
pm2 update
pm2 ecosystem
Conclusion
PM2 gives you:
- Cluster mode to use all CPUs
- Auto-restart on crash or memory overrun
- Zero-downtime deployments via reload
- Centralized rotated logs
- Boot startup
Going further:
- Use PM2 Plus for remote monitoring
- Migrate to systemd + Node.js cluster module for more system-native approach
- For serverless, consider AWS Lambda or Cloudflare Workers
Resources
- PM2 official docs: https://pm2.keymetrics.io/docs/usage/quick-start/
- Source: https://github.com/Unitech/pm2
- Node.js cluster: https://nodejs.org/api/cluster.html
- Clinic.js: https://clinicjs.org

















