Skip to content
Logo
Table of contents
VeryCloud
DocumentationVPS Linux

Node.js + PM2: Node apps in production

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.

Category
VPS Linux
Level
Advanced
Reading time
4 min
Published
May 17, 2026

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-redis or 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

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members