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 --nostreamHow do you catch a restart loop or a memory leak?
| Symptom | What you see | Usual cause | First command |
|---|---|---|---|
| Restart loop | Restart count climbing, uptime always seconds or minutes | Uncaught exception on a specific request, missing env var, port already in use | pm2 logs <app> --err --lines 100 --nostream |
| Memory leak | RSS grows for hours, drops to zero at a restart, grows again | Listeners or caches that never release, a closure holding request bodies | pm2 describe <app>; node --inspect with a heap snapshot on staging |
| Stuck process | Status online, 504s from nginx, CPU at 100 % on one core | A 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 deploy | 502s start at the deploy time, restart count 1 | Build failed, dependency missing, new env var not set | pm2 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 bashThere 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.