NodeJSDebugging with Chrome DevTools

Debugging with Chrome DevTools

Because Node's Inspector speaks the Chrome DevTools Protocol, you can attach the full Chrome DevTools UI to a Node process — the same Sources, Console, Memory, and Performance panels you'd use for a web page, now pointed at your server code. This is especially valuable for the things a code editor's debugger doesn't do as well: CPU profiling with flame charts and heap snapshots for memory analysis. This page covers connecting via chrome://inspect, the panels, and using DevTools to find performance and memory problems.

Connecting DevTools to Node

Bash
# 1. Start Node with the inspector:
node --inspect app.js          # or --inspect-brk to pause on the first line

# 2. In Chrome, open:
chrome://inspect

# 3. Under "Remote Target", click "inspect" on your Node process.
#    (Or click the green Node.js logo for a dedicated DevTools window.)
Debugger listening on ws://127.0.0.1:9229/8f3a...
For help, see: https://nodejs.org/en/docs/inspector
`chrome://inspect` auto-discovers local Node inspectors — click 'inspect' to attach
Navigate to `chrome://inspect` in Chrome (or Edge) and it lists every local process listening on an inspector port — your `--inspect`ed Node app appears under "Remote Target". Click **inspect** and a DevTools window opens bound to that process. The dedicated "Open dedicated DevTools for Node" link gives a window that auto-reconnects across restarts, handy with `nodemon`. If your target isn't local (a remote server, a container), add its `host:port` under "Configure…" — but reach remote inspectors through an [SSH tunnel](/nodejs/node-inspector), never by exposing the port.
The panels that matter for Node

Panel

Use for

Sources

Breakpoints, step-through, watch, call stack, scope inspection

Console

A REPL in the current breakpoint scope — evaluate any expression

Memory

Heap snapshots & allocation timelines — find memory leaks

Performance

CPU profile with flame charts — find hot functions & blocked event loop

The Console evaluates in the paused frame's scope — inspect and even mutate live state
When execution is paused at a breakpoint, the DevTools **Console** isn't a separate sandbox — it evaluates expressions in the scope of the currently selected stack frame. You can read any in-scope variable, call functions, drill into deep objects interactively (far better than `util.inspect`), and even reassign values to test a hypothesis on the fly. Combined with the **Sources** panel's step controls and the live **Scope**/**Watch** views, this turns debugging into an interactive investigation rather than a log-and-rerun loop.
CPU profiling — find what's slow
  • Open the Performance panel → click Record → exercise the slow operation → Stop.

  • DevTools shows a flame chart: width = time spent, so the widest bars are your hottest code paths.

  • Look for wide, flat plateaus — a single synchronous function eating CPU and blocking the event loop.

  • Switch to the Bottom-Up / Call Tree view to rank functions by self-time vs total time.

  • Alternatively capture a profile programmatically with the node --cpu-prof flag and load the .cpuprofile file.

Bash
# Write a CPU profile to disk without DevTools, then open it in the Performance panel:
node --cpu-prof --cpu-prof-dir=./profiles app.js
# Produces ./profiles/CPU.<date>.cpuprofile  →  drag into DevTools Performance tab
A wide bar in the flame chart that runs synchronously is a blocked event loop — the worst Node sin
In a Node CPU profile, the thing to hunt for is a wide, *synchronous* bar: a function that holds the [single thread](/nodejs/event-loop) for a long stretch. While it runs, Node can't process any other request — the whole server stalls. Common culprits are synchronous crypto, large `JSON.parse`/`stringify`, regex backtracking, and tight CPU loops. The flame chart's width-equals-time layout makes these obvious. The fix is to make the work asynchronous, break it into chunks, or move it to a [worker thread](/nodejs/worker-threads) so the main loop stays free. [Profiling](/nodejs/profiling) goes deeper on interpreting these.
Heap snapshots — find memory leaks
  • Open the Memory panel → choose Heap snapshotTake snapshot.

  • Take a second snapshot after running the suspected leaking operation many times.

  • Use the Comparison view to see which object types grew between snapshots — that growth is your leak.

  • Click a growing object and inspect Retainers — the reference chain keeping it alive (the cause).

  • The Allocation instrumentation on timeline view shows allocations live as you interact.

Compare two snapshots — objects that keep growing across them are the leak
A single heap snapshot tells you what's in memory; the *leak* shows up in the **diff** between two snapshots taken before and after repeating an operation. Sort the comparison by "Delta" (or "# New") to find object types that keep accumulating and never get collected — that sustained growth is the leak's signature. Then follow the **Retainers** chain to see *what* is holding references to those objects (a global cache, an event listener never removed, a closure), which is the actual bug. [Finding Memory Leaks](/nodejs/memory-leaks) walks through a full worked example.
Profiling adds overhead and pausing stops the world — be careful on production processes
DevTools is powerful but intrusive. Hitting a breakpoint **pauses the entire process**, so it stops serving every request while paused — fine in development, dangerous on a live production instance. CPU profiling and heap snapshots add measurable overhead and snapshots can briefly freeze the process while V8 walks the heap. For production diagnosis, prefer lower-impact approaches: capture a [diagnostic report](/nodejs/diagnostics) or a one-shot `--cpu-prof`/heap snapshot on a single instance pulled out of rotation, rather than attaching an interactive debugger to a process serving live traffic.
Next
Capture crash and state information without attaching anything: [Diagnostics & Diagnostic Report](/nodejs/diagnostics).