DEV Community

khimananda Oli
khimananda Oli

Posted on • Originally published at khimananda.com

Running Your Node.js Application with PM2

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
Enter fullscreen mode Exit fullscreen mode

That's it. PM2 is managing the process.

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

Cluster Mode — Use All CPU Cores

If your app is stateless (no in-memory sessions), cluster mode spawns one process per CPU core:

pm2 start server.js --name myapp -i max
Enter fullscreen mode Exit fullscreen mode
┌────┬──────┬─────────┬───┬─────┬──────────┐
│ id │ name │ mode    │ ↺ │ cpu │ memory   │
├────┼──────┼─────────┼───┼─────┼──────────┤
│ 0  │ myapp│ cluster │ 0 │ 0%  │ 42.1 MB  │
│ 1  │ myapp│ cluster │ 0 │ 0%  │ 41.8 MB  │
│ 2  │ myapp│ cluster │ 0 │ 0%  │ 42.3 MB  │
│ 3  │ myapp│ cluster │ 0 │ 0%  │ 41.5 MB  │
└────┴──────┴─────────┴───┴─────┴──────────┘
Enter fullscreen mode Exit fullscreen mode

Or specify a fixed count:

pm2 start server.js --name myapp -i 2
Enter fullscreen mode Exit fullscreen mode

With Environment Variables

PORT=3000 NODE_ENV=production pm2 start server.js --name myapp -i max
Enter fullscreen mode Exit fullscreen mode

Or pass them explicitly:

pm2 start server.js --name myapp --env production \
  --node-args="--max-old-space-size=512" \
  -- --port 3000
Enter fullscreen mode Exit fullscreen mode

With a .env File

If your app uses dotenv, just make sure the .env file is in the app's working directory. PM2 runs from the directory where you execute the start command.

cd /opt/myapp
# .env file sits here alongside server.js
pm2 start server.js --name myapp
Enter fullscreen mode Exit fullscreen mode

Memory Limit — Auto Restart on OOM

Set a memory ceiling so a leaky app doesn't eat the whole server:

pm2 start server.js --name myapp --max-memory-restart 512M
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

In fork mode (single instance), reload behaves like restart — there will be a 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
Enter fullscreen mode Exit fullscreen mode

Save the Process List

After starting your app, always save:

pm2 save
Enter fullscreen mode Exit fullscreen mode

This writes the current process list to ~/.pm2/dump.pm2. On reboot, the startup script from Part 1 calls pm2 resurrect and restores everything.

Skip pm2 save and your app won't come back after a reboot. Simple as that.


Managing Multiple Applications

PM2 handles multiple apps without issue:

pm2 start /opt/app1/server.js --name api -i 2
pm2 start /opt/app2/app.js --name frontend
pm2 start /opt/app3/worker.js --name worker

pm2 save
Enter fullscreen mode Exit fullscreen mode
┌────┬──────────┬─────────┬───┬─────┬──────────┐
│ id │ name     │ mode    │ ↺ │ cpu │ memory   │
├────┼──────────┼─────────┼───┼─────┼──────────┤
│ 0  │ api      │ cluster │ 0 │ 0%  │ 42.1 MB  │
│ 1  │ api      │ cluster │ 0 │ 0%  │ 41.8 MB  │
│ 2  │ frontend │ fork    │ 0 │ 0%  │ 38.5 MB  │
│ 3  │ worker   │ fork    │ 0 │ 0%  │ 35.2 MB  │
└────┴──────────┴─────────┴───┴─────┴──────────┘
Enter fullscreen mode Exit fullscreen mode

Monitoring

Real-Time Dashboard

pm2 monit
Enter fullscreen mode Exit fullscreen mode

Terminal UI showing CPU, memory, logs, and metadata per process.

Quick Health Check

pm2 show myapp
Enter fullscreen mode Exit fullscreen mode

Outputs uptime, restart count, memory usage, log file paths, and more.


Troubleshooting

App crash-looping:

pm2 logs myapp --err --lines 100
Enter fullscreen mode Exit fullscreen mode

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

Port already in use:

sudo lsof -i :3000
Enter fullscreen mode Exit fullscreen mode

PM2 shows errored status:

pm2 describe myapp
Enter fullscreen mode Exit fullscreen mode

High restart_time = crash loop. Check error logs.


What We Have So Far

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

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

Follow for Part 3 — Reverse Proxy with Nginx and Apache2

Top comments (0)