Running Your Node.js Application with PM2

Khimananda Oli 3 min read DevOps
Running Your Node.js Application with PM2

Running Your Node.js Application with PM2

Cluster mode, memory limits, zero-downtime reloads, and managing multiple apps — Part 2 of 3

In Part 1, we installed NVM, PM2, configured log rotation, and set up startup scripts. Now let's run the actual application.

Prerequisites

  • NVM, Node.js, and PM2 installed (Part 1)
  • A Node.js application with a server.js or app.js entry point
  • Dependencies installed (npm install or npm ci)

Start the Application

Single Instance

cd /opt/myapp
pm2 start server.js --name myapp

That's it. PM2 is managing the process.

pm2 status
┌────┬────────┬──────┬───┬─────┬────────┐
│ id │ name   │ mode │ ↺ │ cpu │ memory │
├────┼────────┼──────┼───┼─────┼────────┤
│ 0  │ myapp  │ fork │ 0 │ 0%  │ 45.2 MB│
└────┴────────┴──────┴───┴─────┴────────┘

Cluster Mode — Use All CPU Cores

For production, run in cluster mode to utilize all available CPU cores:

pm2 start server.js --name myapp -i max

-i max spawns one worker per CPU core. You can also specify a number:

pm2 start server.js --name myapp -i 4

Your app must be stateless for cluster mode to work correctly. Don't rely on in-memory sessions or local file writes that need to be shared across workers.

Pass Environment Variables

pm2 start server.js --name myapp --env production

Or inline:

NODE_ENV=production PORT=3000 pm2 start server.js --name myapp

Set Memory Limits

Automatically restart if memory exceeds 512MB:

pm2 start server.js --name myapp --max-memory-restart 512M

When any instance exceeds 512MB, PM2 restarts it automatically.

Watching for File Changes (Dev/Staging Only)

pm2 start server.js --name myapp --watch --ignore-watch="node_modules logs"

Don't use --watch in production. A deployment that touches multiple files triggers multiple restarts. Use pm2 reload instead.

Zero-Downtime Reload

In cluster mode, reload restarts workers one at a time — no dropped requests:

pm2 reload myapp

In fork mode (single instance), reload behaves like restart — brief downtime.

Verify the Application

# PM2 status
pm2 status

# Port listening?
ss -tlnp | grep 3000

# Hit the app
curl http://localhost:3000

# Logs
pm2 logs myapp --lines 50

Running Multiple Apps

pm2 start api.js --name api -i max
pm2 start worker.js --name worker
pm2 start scheduler.js --name scheduler

All managed together:

pm2 status
pm2 reload all
pm2 logs

Detailed Process Info

pm2 show myapp

Troubleshooting

App crash-looping:

pm2 logs myapp --err --lines 100

Common culprits: missing env vars, port conflicts, unhandled promise rejections.

Port already in use:

sudo lsof -i :3000

PM2 shows errored status:

pm2 describe myapp

High restart_time = crash loop. Check error logs.

What We Have So Far

  • ✅ Part 1: NVM + PM2 + startup + log rotation
  • ✅ Part 2: App running, cluster mode, memory limits, zero-downtime reloads

The app is running on localhost:3000 but not exposed to the internet yet.

Next up — Part 3: Reverse Proxy with Nginx and Apache2

Follow me for Part 3, or check out more DevOps content on khimananda.com.