Monitoring

How do you monitor a Node.js app in production (PM2, memory, restarts)?

Monitor Node.js apps in production: what to watch beyond pm2 monit, how to catch restart loops and memory leaks, and a 30-second setup with no npm package.

In short

Monitoring a Node.js app in production means watching five things: the process (is it up, how often has PM2 restarted it, how much memory it holds), the host it runs on (memory, disk, CPU), the reverse proxy in front of it (502s and 504s mean the app stopped answering), its error log, and what changed before a problem started. pm2 monit shows the first on one box; a monitoring agent shows all five across every server and alerts you.

What should you monitor on a Node.js server?

A Node.js process fails quietly. It does not segfault; it throws, PM2 restarts it, and the site is back before anyone looks. The signs are in the counters, not the uptime check:

  • •Process state and restart count: a PM2 app with 40 restarts since yesterday is crashing on every N-th request, even if it is "online" right now
  • •Memory per process: Node holds its heap until the garbage collector runs, so steady growth over hours, not a spike, is the leak signature
  • •Host memory and swap: one leaking worker takes the whole box into swap, and every other service on it gets slow first
  • •The reverse proxy: nginx or Apache log 502 (upstream refused) and 504 (upstream timed out) when the app is down or stuck; they are the earliest user-visible symptom
  • •The error log: unhandled rejections, ECONNREFUSED to a database, EADDRINUSE after a bad restart, and "JavaScript heap out of memory"
  • •Changes: the commit that was deployed, the Node or npm upgrade, the config edit in the minutes before the first error

Request tracing (APM) is a different question. It tells you which route is slow inside the app. The list above tells you the app is down, leaking or crash-looping, which is what pages you at night.

Is pm2 monit enough?

On one server, for the person logged in to it, nearly. `pm2 ls` shows every app with its status, restart count, uptime, CPU and memory; `pm2 monit` adds a live view; `pm2 logs <app> --err` shows the error stream. What it does not do:

  • •Tell you anything when you are not logged in: there is no alert, no history, and PM2 Plus is a separate paid product
  • •See the host: a full disk or a swapping box is invisible from inside PM2
  • •Cover the other servers: at five servers the question "which app is restarting" has five answers to collect
  • •Explain the restart: the count goes up; the reason is in a log file you have to open
pm2 ls
pm2 describe api | grep -E "restarts|uptime|memory|error log"
pm2 logs api --err --lines 200 --nostream

How do you catch a restart loop or a memory leak?

SymptomWhat you seeUsual causeFirst command
Restart loopRestart count climbing, uptime always seconds or minutesUncaught exception on a specific request, missing env var, port already in usepm2 logs <app> --err --lines 100 --nostream
Memory leakRSS grows for hours, drops to zero at a restart, grows againListeners or caches that never release, a closure holding request bodiespm2 describe <app>; node --inspect with a heap snapshot on staging
Stuck processStatus online, 504s from nginx, CPU at 100 % on one coreA synchronous loop or a blocked event loop (JSON.parse of a huge body, sync fs call)pm2 describe <app>; kill -USR1 for a CPU profile, or restart and read the last request in the access log
Down after deploy502s start at the deploy time, restart count 1Build failed, dependency missing, new env var not setpm2 logs <app> --lines 50 --nostream, then the deploy log

The four Node.js production failures and how each one shows up

The thing that separates a five-minute fix from a two-hour one is the timeline: knowing that restarts started at 14:32 and the deploy was at 14:31 answers the question before you open a log.

What does Opservo show for a Node.js app?

The Opservo agent discovers PM2 apps by their daemon (every user’s PM2, not only root’s), systemd units and Docker containers, so a Node app running any of those ways appears on the Services tab with its status, restart count, uptime, CPU and memory, sortable by restarts. It reads package.json to name the app and its framework (Express, Fastify, Next.js, Nest, Nuxt, Remix, SvelteKit and others) with the version and the git commit, so a deploy is a visible change on the timeline. PM2 error logs, nginx and Apache logs are tailed and clustered by pattern, so a new "heap out of memory" line or a burst of 502s is one finding with a count, not two hundred lines.

  • •Host facts next to the app: memory and swap, disk space, load, with the health score naming the rule that lost points
  • •A finding when nginx is proxying to an upstream that refuses connections, with the service names to check
  • •A recommendation when Node.js apps are found but PM2 is not installed, so they will not come back after a crash or reboot
  • •Quick actions for `pm2 restart <app>`, `pm2 logs <app>` and `pm2 show <app>` from the dashboard, on servers where you turned remote actions on
  • •Alerts on Slack, email, Telegram, Discord, PagerDuty or a webhook when a service fails, memory pressure builds or a log error recurs
curl -sSL https://api.getopservo.com/install.sh | sudo bash

There is no npm package to add and nothing in the app changes. The agent is one Go binary, outbound HTTPS only, read-only unless you switch on remote actions for that server. What it cannot do is trace a request through your code: for per-route latency inside the app you still want an APM, and the two can run side by side.

What should alert, and what should not?

  • •Alert: a PM2 app in the stopped or errored state, a restart count that keeps rising, a 502/504 burst, a "heap out of memory" error, host memory pressure
  • •Do not alert: a single restart at deploy time, CPU briefly at 100 % during a build, memory that is high but flat
  • •Correlate: when the disk is full or the host is out of memory, the app failures are symptoms; one alert should name the cause and list the rest under it

Last updated 2026-10-20.

FAQ

Related questions

Does Opservo monitor Node.js without an npm package or code change?+

Yes. The agent reads PM2, systemd and Docker from the host, tails the error logs and reads package.json for the app name, framework and version. Nothing is added to the application and no request is instrumented.

Is this an APM like New Relic or Datadog APM?+

No. Opservo shows whether the process is up, restarting, leaking or stuck, what the proxy and logs say, what changed, and how the host is doing. It does not trace requests or measure per-route latency inside the app. Teams that need both run an APM alongside it.

Does it work if the app runs under systemd or in Docker instead of PM2?+

Yes. systemd units and Docker containers are discovered the same way, with status, restart count and memory, and Docker crash loops are a finding on their own.

How is a Node.js memory leak different from high memory use?+

High memory that stays flat is the app’s working set and is fine if the host has room. A leak is memory that grows for hours and only drops when the process restarts. The agent reports memory per process on every heartbeat, so the shape of the curve is visible on the server page rather than guessed from one reading.