Node ships with two ways to debug without installing anything. node inspect is a minimal command-line debugger for setting breakpoints and stepping through code in the terminal. More powerfully, the --inspect flag activates the Inspector — a server speaking the Chrome DevTools Protocol — which lets full GUI debuggers (Chrome DevTools, VS Code) attach over a WebSocket. This page covers both, the all-important --inspect vs --inspect-brk difference, attaching to a running or remote process, and the serious security implications of leaving the inspector open.
The CLI debugger: node inspect
Bash
node inspect app.js
app.js
JS
function add(a, b) {
debugger // execution pauses here when run under the debugger
return a + b
}
console.log(add(2, 3))
< Debugger listening on ws://127.0.0.1:9229/...
break in app.js:1
> 1 function add(a, b) {
2 debugger
3 return a + b
debug>
Command
Action
cont / c
Continue until the next breakpoint
next / n
Step to the next line (step over calls)
step / s
Step into a function call
out / o
Step out of the current function
repl
Open a REPL to evaluate variables in the current scope
setBreakpoint(N) / sb(N)
Set a breakpoint at line N
watch(expr)
Watch an expression's value as you step
A `debugger;` statement is a breakpoint in code — ignored unless a debugger is attached
The `debugger` statement is the simplest breakpoint: when the process runs under any debugger, execution pauses there; when run normally, it's a no-op. Inside `node inspect` you drive execution with single-letter commands — `n` (next line), `s` (step in), `o` (step out), `c` (continue) — and drop into `repl` to inspect or evaluate anything in the current scope. The CLI debugger is always available with zero setup, which makes it handy on a remote server, but its ergonomics are spartan compared to the GUI debuggers that attach via the Inspector.
The Inspector: --inspect vs --inspect-brk
Bash
# Start the inspector and run immediately (attach whenever you like):
node --inspect app.js
# Start the inspector and PAUSE on the first line until a debugger attaches:
node --inspect-brk app.js
# Custom host/port (default is 127.0.0.1:9229):
node --inspect=127.0.0.1:9230 app.js
Debugger listening on ws://127.0.0.1:9229/0f2c...-...-...
For help, see: https://nodejs.org/en/docs/inspector
Use `--inspect-brk` for short-lived scripts — `--inspect` alone may finish before you attach
The difference is timing. `--inspect` opens the debugger port but lets the program run straight away — fine for a long-running server you'll attach to while it's up, but for a script that finishes in milliseconds the process exits before you've connected, and you debug nothing. `--inspect-brk` opens the port *and breaks on the very first line*, holding the process until a debugger attaches and tells it to continue — exactly what you want for short scripts, startup-code bugs, or anything where you must be attached from line one. Reach for `--inspect-brk` whenever you need to catch early execution.
Attaching to a running process
Bash
# A process already running WITHOUT --inspect? Send it SIGUSR1 to open the port:
kill -SIGUSR1 <pid> # Node starts listening on 9229
# Then attach a debugger (Chrome: visit chrome://inspect, or VS Code "attach").
`SIGUSR1` opens the inspector on an already-running process — useful for live diagnosis
Node treats the `SIGUSR1` signal as "start the inspector now", so you can enable debugging on a process that was launched *without* `--inspect` — invaluable when a production-like process is misbehaving and you didn't plan to debug it. Once the port is open, attach Chrome DevTools or VS Code and set breakpoints in the live process. Be cautious doing this on a real production server: hitting a breakpoint *pauses the entire process*, which means it stops serving all requests while paused.
The CDP architecture
Text
┌────────────────┐ Chrome DevTools Protocol ┌──────────────────────┐
│ Your debugger │ ◀── WebSocket (ws://...) ──▶ │ Node Inspector │
│ (DevTools, │ JSON messages │ (inside the V8 │
│ VS Code, CLI) │ │ runtime, port 9229) │
└────────────────┘ └──────────────────────┘
sets breakpoints, pauses execution,
reads variables reports state back
The Inspector speaks the Chrome DevTools Protocol — any CDP client can attach
`--inspect` starts a small server inside the V8 runtime that speaks the **Chrome DevTools Protocol (CDP)** over a WebSocket. Because the protocol is standardized, *any* CDP-speaking client can attach: Chrome DevTools, VS Code's debugger, WebStorm, or the `ndb`/`node-inspect` tools. This is why the same `--inspect` flag powers every GUI debugging experience — they're all just different front-ends talking the same protocol to the same inspector. Understanding this makes the [VS Code](/nodejs/vscode-debugging) and [Chrome DevTools](/nodejs/chrome-devtools) setups feel like two views of one mechanism.
Security: never expose the inspector port
The inspector port is full remote code execution — bind to localhost, never 0.0.0.0
The Inspector grants **complete control** over the Node process: an attacker who can reach the port can read all memory (including secrets and credentials), modify execution, and run arbitrary code — it is effectively a remote shell. Node defaults to binding `127.0.0.1` (localhost only) for exactly this reason. **Never** start it on a public interface (`--inspect=0.0.0.0`) or forward the port openly. To debug a remote server safely, leave the inspector bound to localhost and reach it through an **SSH tunnel** (`ssh -L 9229:localhost:9229 user@host`). Never enable `--inspect` in production by default, and ensure the port is firewalled.
Next
Get a full graphical debugger inside your editor: [Debugging in VS Code](/nodejs/vscode-debugging).