How to monitor Node.js apps
Monitor a Node.js app with an HTTP health check from outside, then from inside: route latency and error rate, process restarts, heap use against the V8 limit, and uncaught exceptions. Vigil's Node SDK or any OpenTelemetry SDK sends traces with slow pg, mysql2 and ioredis calls; a Sentry SDK sends errors; the agent watches the process and memory.
What can go wrong with Node.js apps
- Event loop blocked. One synchronous operation (a large
JSON.parse, a sync file read, a CPU-heavy loop, a catastrophic regex) stalls every request on that process, so latency jumps for all routes at once. - Heap exhaustion. A leak grows the heap until V8 aborts with
FATAL ERROR: … JavaScript heap out of memory. The process manager restarts it and the cycle repeats. - Unhandled promise rejections. Since Node 15, an unhandled rejection crashes the process by default. In-flight requests on that process fail.
- Restart loops. pm2, systemd or Kubernetes restart the crashed process so quickly that uptime checks rarely catch it; the restart counter does.
- Slow dependencies. A slow database query or third-party API holds connections from a small pool, and requests queue behind them.
The signals that matter, with alert thresholds
| Signal | Alert when | Why it matters |
|---|---|---|
| Health endpoint | non-2xx twice, or > 1 s | Users are affected. |
| p95 latency per route | > 2× the weekly normal | Event loop or dependency trouble. |
| 5xx rate | > 1-5% of requests | Errors, crashes. |
| Process restarts | any unplanned | Crash loop. |
| Heap used / heap limit | > 85% sustained | OOM abort is next. |
| New exception types | first occurrence | A new bug, often from a deploy. |
Thresholds are starting points. Tune them after a week of data: an alert nobody acts on should be raised or removed.
How to check Node.js apps by hand
curl -fsS -o /dev/null -w '%{http_code} %{time_total}s\n' http://127.0.0.1:3000/health
node -p "require('v8').getHeapStatistics().heap_size_limit / 1048576"
ps -o pid,rss,etime,args -C node
pm2 list pm2 describe my-app | grep -E 'restarts|memory'
How to watch Node.js apps with Vigil
- In New monitor create an API check on
/healthwith an assertion such asjson status == ok, and an Uptime monitor on the public site. - Add the host under Servers and run the one-line installer it shows (
curl -fsSL https://puzaricloud.in/install-server-agent.sh | sh -s -- --token <token>). The agent is one Python file with no dependencies; CPU, memory, disk, inode and load rules start working with the first report. Addnode(or your unit name) to the processes that must be running, and ship the app's log files or journal unit. - Add tracing: download
/vigil-apm.jsand callrequire('./vigil-apm').init({ service: 'web' })first thing at start-up. It instruments http(s), fetch, pg, mysql2 and ioredis and sends spans to the agent on 127.0.0.1:9111. Or use the OpenTelemetry Node SDK with the OTLP endpoint (quick start). - You get routes with latency histograms, Apdex, slow queries ranked by total time, slow outbound calls, N+1 detection and the impact of each deploy.
- Create a project under Errors and use its DSN with
@sentry/node: uncaught exceptions and rejections arrive grouped, with alerts on new issues and regressions.
Every alert goes to the channels you pick: email, Slack, Telegram, Discord, Microsoft Teams, Pushover, webhooks or on-call paging. Failures are confirmed by a second check before anyone is woken up.
Node.js apps monitoring FAQ
How do I find what blocks the event loop?
Look for the route whose spans are slow with no slow child span: the time went into synchronous JavaScript. Reproduce with node --cpu-prof and open the profile in Chrome DevTools.
Should I raise --max-old-space-size?
Only if the app legitimately needs more memory and the host or container has it. If heap grows until the limit no matter what, it is a leak, and a bigger heap only delays the crash.
Does this work with Express, Fastify, NestJS and Next.js?
Yes, the SDK instruments Node's http layer underneath them. For Next.js specifics see how to monitor Next.js.