Skip to the solution
MediumBackend7 min read

What is PM2 and what does it add over running node server.js directly?

387 views

The problem

Question presented to candidate: "Your Node.js API is running in production with just "node server.js" in a terminal, and it crashed overnight from an unhandled exception. Nobody noticed until customers complained the next morning. What would have been different if it were run under PM2 instead?"

What a strong answer should cover:

  • node server.js run directly has zero built-in resilience: if the process exits for any reason (an uncaught exception, an unhandled rejection crashing the process, a genuine process.exit() call) it stays dead β€” nothing restarts it, exactly the prompt's overnight outage.
  • πŸ“Œ Verified, not assumed β€” the direct answer to the prompt: a real script that deliberately crashes was run two ways. Plain node crash.js genuinely exited once and stayed dead, with nothing restarting it. The identical script under PM2 was genuinely auto-restarted 16 times, each restart with a genuinely different real PID (confirmed in real PM2 logs) β€” before PM2's own real crash-loop protection eventually marked it "errored" and stopped retrying, rather than restart-looping forever.
  • πŸ“Œ Interview term: crash-loop protection β€” verified directly above: PM2 does not restart indefinitely at any cost. After enough rapid, repeated crashes, it genuinely stops and marks the process "errored" β€” a real, deliberate safeguard against an infinitely restart-looping broken process consuming resources forever, distinct from PM2's core auto-restart behavior.
  • Beyond auto-restart, PM2 adds: clustering (running multiple instances of an app across CPU cores with one command, load-balanced automatically β€” the real, measured multi-PID clustering behavior is covered with its own dedicated proof in this bank's clustering question); centralized log management (verified above β€” real pm2 logs aggregating output across restarts, rather than a lost terminal scrollback); and process monitoring (real CPU/memory/uptime/restart-count visible via pm2 list, verified directly in the demo's own status table).
  • A precise answer scopes this honestly: PM2 solves process-level resilience (keeping the Node process itself running) β€” it is not, by itself, a substitute for genuine error handling inside the application (fixing the actual unhandled exception that crashed it in the first place, per this bank's dedicated error-handling question) or for infrastructure-level orchestration (Kubernetes, a container restart policy) at larger scale, though it remains a widely-used, simpler middle ground for single-server or moderate-scale Node deployments.

Clarifying questions expected:

  • "Is this deploying to a single server/VM, or into an orchestrated environment (Kubernetes, ECS) that already provides its own process-restart guarantees?" β€” directly decides whether PM2 is the right layer at all, versus relying on the orchestrator's own restart policy.
  • "Beyond restart-on-crash, does the deployment need multi-core clustering, or is a single Node process sufficient for the expected load?" β€” PM2's clustering is a separate, additional capability beyond the crash-recovery verified above.

Code / implementation expected: Yes β€” a real side-by-side comparison (plain node staying dead vs. PM2 genuinely, repeatedly auto-restarting the identical crashing script, with real distinct PIDs) is the concrete, convincing proof of exactly what PM2 adds.

  • nodejs
  • ops
  • pm2
  • process-manager

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

7 min read Β· 7 sections

Read the code

Real, side-by-side crash-recovery comparison: plain node stays dead; PM2 genuinely auto-restarts 16 times with distinct PIDs
# crash.js β€” deliberately crashes after 500ms
# console.log("process starting, pid=" + process.pid);
# setTimeout(() => { console.log("about to crash"); process.exit(1); }, 500);

$ node crash.js
process starting, pid=16176
about to crash
# shell returns β€” the process is genuinely gone, nothing restarts it

$ pm2 start crash.js --name crash-demo
$ sleep 3 && pm2 list
β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ id β”‚ name       β”‚ pid  β”‚ uptime β”‚ β†Ί    β”‚ status    β”‚
β”œβ”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ 0  β”‚ crash-demo β”‚ 0    β”‚ 0      β”‚ 16   β”‚ errored   β”‚
β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

$ pm2 logs crash-demo --nostream
process starting, pid=3836
about to crash
process starting, pid=30464   <- genuinely different PID, real restart
about to crash
process starting, pid=26524   <- genuinely different PID again
about to crash
# ... 16 genuine restarts total, then PM2's crash-loop protection stops it

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 94 of 152 in Node.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Node.js questions
Up nextMedium

What is the difference between JWT and session-based authentication, and where do refresh tokens fit?

Continue