Client Area

PM2 for Node.js: Production Process Management Guide

ByDomain India Team
7 min read22 Apr 20263 views

In this article

  • 1Prerequisites
  • 2Install PM2
  • 3Your first PM2 start
  • 4Use an ecosystem file (the right way)
  • 5Cluster mode — use all your CPU cores

PM2 for Node.js: Production Process Management Guide

A fresh node server.js dies the moment you press Ctrl+C. Production needs a process manager that keeps your app running, restarts it when it crashes, spawns multiple workers for multi-core machines, handles logs, and survives server reboots. PM2 is the de-facto standard. This article walks through installing PM2, setting up an ecosystem file, running in cluster mode, log rotation, and the handful of gotchas that catch most teams.

Prerequisites

  • A VPS (shared cPanel hosting does not let you run long-lived processes — PM2 is a VPS-or-above tool). Our VPS hosting plans include root SSH and support PM2.
  • Node.js installed. Run node -v; anything 18.x or above is fine.
  • Your app runnable from the command line (node index.js or npm start).

Install PM2

bash
npm install -g pm2

Global install puts pm2 on the PATH for every user. On a VPS, you typically install it as root once; each app user then uses it for their own app.

Verify:

bash
pm2 --version

Your first PM2 start

From your app directory:

bash
pm2 start server.js --name my-app

Or for npm scripts:

bash
pm2 start npm --name my-app -- start

Inspect the running process list:

bash
pm2 list

You will see a table with app name, status (online / stopped / errored), CPU / memory usage, and restart count.

Other day-one commands:

bash
pm2 logs my-app           # tail logs
pm2 restart my-app        # restart
pm2 stop my-app           # stop but keep the entry
pm2 delete my-app         # remove from PM2's list

Use an ecosystem file (the right way)

Everything you did on the command line should be in a version-controlled config. Create ecosystem.config.js in your project root:

javascript
module.exports = {
  apps: [
    {
      name: 'my-app',
      script: './server.js',
      instances: 2,
      exec_mode: 'cluster',
      watch: false,
      max_memory_restart: '500M',
      env: {
        NODE_ENV: 'development',
      },
      env_production: {
        NODE_ENV: 'production',
      },
      error_file: './logs/err.log',
      out_file: './logs/out.log',
      time: true,
    },
  ],
};

Start with:

bash
pm2 start ecosystem.config.js --env production

Now your team can reproduce the exact run config by reading one file. Commit it to git.

Cluster mode — use all your CPU cores

A single Node process uses one CPU core. On a 4-core VPS, running 4 workers gives you 4× the throughput. PM2 does this in a single config change:

javascript
{
  name: 'my-app',
  script: './server.js',
  instances: 'max',          // use all cores, or set a number like 4
  exec_mode: 'cluster',
}

Zero-downtime reload (old workers drain, new workers start):

bash
pm2 reload my-app

This is different from pm2 restart — reload drains; restart kills.

When cluster mode does NOT work:

  • WebSocket apps without sticky sessions (different workers get different clients; state is split across workers). Use instances: 1 or add a session store like Redis.
  • In-memory caches (each worker has its own cache; invalidation is hard).
  • Scheduled jobs that must run exactly once (cluster mode would run the job N times). Use a queue or a flag.

Log management

By default, PM2 writes logs to ~/.pm2/logs/<app>-out.log and <app>-error.log. Tail them:

bash
pm2 logs my-app             # both stdout and stderr
pm2 logs my-app --err       # errors only
pm2 logs my-app --lines 200 # last 200 lines

Install the log rotation module so logs do not fill your disk:

bash
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 10
pm2 set pm2-logrotate:compress true

This rotates each log at 10 MB, keeps the last 10, and gzips the older ones.

Survive server reboots

By default, PM2 processes disappear when the VPS reboots. Tell PM2 to register itself with systemd:

bash
pm2 startup systemd

This prints a command that starts with sudo env PATH=... — copy and run it as root. Done once, persists forever.

Then save your current process list so PM2 restores it on boot:

bash
pm2 save

Test it: sudo reboot, wait, SSH back in, run pm2 list — your app is already running.

Verify the systemd service:

bash
sudo systemctl status pm2-youruser

Monitoring

Built-in TUI:

bash
pm2 monit

Shows live CPU, memory, logs per process. Fine for quick debugging.

For production observability, forward your logs to a central tool (Datadog, Loggly, or a self-hosted Grafana + Loki stack). pm2-logrotate does not do central aggregation — it only rotates local files.

Memory guard:

javascript
max_memory_restart: '500M',

If the worker exceeds 500 MB RSS, PM2 restarts it. Useful defence against memory leaks while you find the root cause.

Common pitfalls

"Port already in use"

Something is already listening on your port. Could be:

  • A zombie Node process — pm2 list and pm2 delete <old-app-name>
  • Another system process — sudo lsof -i :3000 to find it
  • A previous failed start still holding the port briefly — wait 30 seconds, retry

PM2 processes disappear after reboot

You forgot pm2 save after pm2 startup. Do them in that order: startup first to register the systemd service, then save after your apps are running.

Logs fill the disk

You skipped pm2-logrotate. Install it immediately — unrotated PM2 logs can grow to gigabytes.

Cluster mode breaks WebSockets

Sticky sessions are required in cluster mode for WebSocket apps. Use @socket.io/sticky or deploy behind an nginx ip_hash load balancer. Simpler: use instances: 1 for WebSocket-heavy apps.

pm2 reload fails with "Could not find script"

Cluster mode needs to fork the process. If your script path is relative, PM2 can lose track of the working directory. Use absolute paths in ecosystem.config.js:

javascript
script: '/home/deploy/my-app/server.js',
cwd: '/home/deploy/my-app',

Alternatives and when to pick them

ToolWhen to use
systemdSingle Node app, no cluster mode needed, you prefer native Linux. More typing, zero deps.
SupervisordMulti-language app (Python + Node + shell workers all on one VPS). See our Supervisord guide.
Docker ComposeYou already use Docker and want one tool for all processes.
KubernetesYou have 10+ VPS nodes. Otherwise overkill.
PM2Single-VPS Node shop. Best ergonomics, mature, widely known.

Frequently asked questions

Does PM2 work on shared hosting?

No. Shared hosting does not allow long-lived processes. You need a VPS.

Can I run multiple different Node apps on one VPS with PM2?

Yes. Each app gets its own entry in ecosystem.config.js (or you run pm2 start multiple times). They share PM2's management but run independently.

What is `exec_mode: fork` vs `cluster`?

fork — one process, one worker. Simple. cluster — PM2 uses Node's cluster module to spawn N workers sharing a port. Use cluster to utilise multiple CPU cores.

How do I restart PM2 on code deploy?

pm2 reload my-app (zero-downtime in cluster mode). For single-worker apps, use pm2 restart my-app — there will be a brief unavailability window, usually under a second.

Why is my app using 100% CPU?

Run pm2 monit to see which worker. Then pm2 logs <app> — look for infinite loops, runaway recursion, or a stuck request. node --inspect if you need to attach a debugger.

Can I limit PM2 to use less memory?

Yes. Set max_memory_restart per app. For system-wide limits, use cgroups via systemd's MemoryLimit on the pm2-youruser.service unit.

Is there a paid version?

PM2 Plus (keymetrics.io) — hosted monitoring, alerting, remote-control. Optional; the OSS version is fully featured for most shops.


Questions or stuck? [email protected] — we help VPS customers with PM2 setup as part of standard support.

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket