Process Inspection
top, htop, and lsof for diagnosing port conflicts, runaway Node processes, and resource pressure on Linux hosts running Node.js APIs.
Recipe
Quick-reference recipe card - copy-paste ready.
# Port in use (EADDRINUSE)
lsof -i :3000
# or
ss -tlnp 'sport = :3000'
# Graceful stop
kill -TERM <PID>
sleep 5
kill -KILL <PID> # only if still running
# CPU/memory snapshot
top -b -n 1 | head -20
ps aux | grep nodeWhen to reach for this:
- Local dev:
Error: listen EADDRINUSE: address already in use :::3000 - Production: pod CPU throttling or memory limit kills
- Suspected zombie Node process after crashed test run
- Multiple workers and API on same VM - who owns which port?
Working Example
Port conflict on developer laptop
$ npm run dev
Error: listen EADDRINUSE: address already in use :::3000
$ lsof -i :3000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 48291 dev 23u IPv6 0t... 0t0 TCP *:3000 (LISTEN)
$ kill -TERM 48291
$ npm run dev # succeeds- Prefer
kill -TERM(SIGTERM) so Fastify/Express shutdown hooks run kill -9only when process ignores SIGTERM after 10s
Production pod inspection (Kubernetes)
# Find pod
kubectl get pods -l app=orders-api
# OOM and restart reason
kubectl describe pod orders-api-7f8b9c-xyz | grep -A5 "Last State"
# Top inside container (if image has procps)
kubectl exec -it orders-api-7f8b9c-xyz -- top -b -n 1 | head -15
# Process list
kubectl exec orders-api-7f8b9c-xyz -- ps aux# Typical describe excerpt
Last State: Terminated
Reason: OOMKilled
Exit Code: 137- Exit code 137 often means OOM kill (128 + 9)
- Compare container
memory.limitto Node--max-old-space-sizeif set
Separate API and worker processes
ps aux | grep node
# node dist/server.js ← API port 3000
# node dist/workers/email.js ← no listen port, high CPU during batch- Workers without HTTP should not appear in
lsof -i :3000 - High worker CPU: check BullMQ concurrency and job payload size
htop Tips
htop
# F5 tree view - see parent container PID 1
# F6 sort by MEM% or CPU%
# Filter: F4 type "node"- Single-threaded CPU at 100% on one core may be event loop stall (sync work)
- Memory climb without traffic increase suggests leak - see Incident Triage Skill
Comparison
| Tool | Best for |
|---|---|
| lsof | Which PID owns port 3000 |
| ss | Socket summary without installing lsof |
| top | Quick CPU/mem snapshot (every server) |
| htop | Interactive sorting and tree view |
| kubectl describe | OOMKilled, restart count, limits |
FAQs
lsof not found in Alpine container?
Use ss -tlnp or install lsof in debug image only - not production Dockerfile unless approved.
Node shows many threads in top - normal?
Yes. libuv thread pool and V8 workers create threads. Worry about memory RSS and event loop lag, not thread count alone.
Safe to kill PID 1 in container?
No. Kubernetes restarts the pod. Scale down replica or kubectl delete pod for clean restart after drain.
Related
- CLI Basics for Node - curl, jq, ss
- Linux CLI Best Practices - SIGTERM before SIGKILL
- OOM Killer Response - heap vs RSS
- Runtime Ops Basics - graceful shutdown
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.