cheat sheet

nodemon

Package-level reference for nodemon on npm — watch globs, exec patterns, config files, and how it compares to node --watch and tsx watch.

nodemon

What it is

nodemon is a long-running file-watcher and process supervisor for Node.js. Point it at a script (or any command), give it a watch pattern, and it restarts the process whenever a matching file changes. It's the daily-driver dev tool that turns "edit → save → manually restart → test" into "edit → save → test".

nodemon predates Node's built-in --watch flag (Node 18.11+) by roughly a decade. It still beats the native flag for richer features: config files, debounce / delay, watch-extension lists, custom exec commands (run tsx, ts-node, or even python under it), shell commands on event hooks, and per-event signals. For a simple dev server, node --watch is enough; for anything else, nodemon remains the standard.

Install

bash
# Project-local (recommended — pin to lockfile)
npm install -D nodemon
pnpm add -D nodemon
yarn add -D nodemon
bun add -d nodemon

Output: nodemon binary in node_modules/.bin/ — invoke via npx nodemon or in an npm script.

bash
# Global (acceptable — nodemon is stable and stateless)
npm install -g nodemon

Output: nodemon on user PATH; per-project pinning still preferred for CI parity.

bash
# One-off
npx nodemon src/server.js

Output: downloads nodemon to the npm cache and runs.

Versioning & Node support

  • Current line is 3.x. The 3.0 release (mid-2023) dropped support for Node 10 and refactored the watch backend for Node 16+ APIs.
  • Requires Node 10 or newer; in practice everything modern works fine.
  • Pure JS, no native bindings — installs without a build step on any platform.
  • SemVer respected; minor releases mostly tweak watch defaults and CLI parsing.
  • Maintenance is steady but slow — no big roadmap, mostly bug fixes.

Package metadata

  • Maintainer: Remy Sharp (remy) + small contributor pool
  • Project home: github.com/remy/nodemon
  • Docs: nodemon.io
  • npm: npmjs.com/package/nodemon
  • License: MIT
  • First released: 2010
  • Downloads: ~50M weekly — among the most-installed dev tools on npm

Peer dependencies & extras

nodemon ships with everything it needs — chokidar for cross-platform file-watching, plus a small CLI helper layer. No peers required.

CompanionRole
tsx / ts-nodenodemon doesn't transpile TypeScript itself — pair with --exec tsx src/server.ts to run TS sources.
dotenv-clinodemon --exec "dotenv -- node src/server.js" to load env on each restart.
@types/nodemon(none — nodemon doesn't export a programmatic API people import)

Alternatives

ToolTrade-off
node --watch (built-in, Node 18.11+)Native, zero deps. Less configurable — no debounce, no exec hook, no event scripts. Good enough for simple servers.
tsx watchTS-aware watch driven by the import graph. Faster restarts; only watches what's actually imported. No exec hook.
chokidar-cliLower-level — runs an arbitrary command on file change with no Node-specific affordances. Useful for non-Node watchers.
pm2Process supervisor; usually overkill for dev. Better suited to production restart-on-crash, not file-change watching.
watchexec (non-Node)Cross-language Rust file-watcher CLI. Great if you need to watch a polyglot repo.
vite dev / next devFramework-specific dev servers with built-in HMR. Use them where they apply; nodemon for plain Node servers.

Real-world recipes

Simple watch on a Node server

bash
nodemon src/server.js

Output:

text
[nodemon] 3.1.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node src/server.js`
Server listening on :3000

Type rs + Enter in the terminal to force a manual restart. Default watches the cwd recursively for .js, .mjs, .cjs, .json.

Debounce noisy editors and delay restart

Some editors emit multiple save events per save (atomic write, swap files). --delay debounces and prevents thrashing:

bash
nodemon --delay 500ms src/server.js
nodemon --delay 2 src/server.js          # 2 seconds

Output:

text
[nodemon] file changed: src/api/users.js
[nodemon] file changed: src/api/users.js   ← debounced; only one restart
[nodemon] restarting due to changes...

A --delay 500ms value is usually right; tune up if your editor saves in waves.

Run TypeScript via tsx under nodemon

When you want nodemon's globbing rules (or hooks) AND TypeScript:

bash
nodemon --watch src --ext ts,tsx,json --exec "tsx src/server.ts"

Output:

text
[nodemon] watching extensions: ts,tsx,json
[nodemon] starting `tsx src/server.ts`
Server listening on :3000
[nodemon] restarting due to changes...
[nodemon] starting `tsx src/server.ts`

Use this pattern when tsx watch's import-graph rules miss files you care about (e.g. .json config, fixtures), or when you want to chain dotenv -- tsx ... and add event hooks.

Watch a non-Node command (or anything)

nodemon doesn't require Node. Use it as a generic file-watcher with --exec:

bash
# Re-run a Python script when any .py file changes
nodemon --ext py --exec "python ./main.py"

# Re-build a Go binary and run it
nodemon --ext go --exec "go run ./cmd/server"

# Restart a docker compose stack
nodemon --watch ./docker --ext yml --exec "docker compose up -d --build"

Output:

text
[nodemon] watching extensions: py
[nodemon] starting `python ./main.py`
[nodemon] restarting due to changes...

Project-level nodemon.json config

json
{
  "watch": ["src", "config"],
  "ext": "ts,tsx,json,yaml",
  "ignore": ["src/**/*.test.ts", "src/__fixtures__/*"],
  "delay": "500ms",
  "exec": "tsx src/server.ts",
  "events": {
    "restart": "echo 'Restarting at $(date)' >> nodemon.log"
  },
  "env": {
    "NODE_ENV": "development",
    "LOG_LEVEL": "debug"
  }
}
bash
nodemon          # picks up nodemon.json automatically

Output:

text
[nodemon] config: nodemon.json
[nodemon] watching: src, config
[nodemon] starting `tsx src/server.ts`

nodemon.json lives in the project root. Alternative: add a nodemonConfig block in package.json — same schema, no extra file.

Per-event hook scripts

The events block fires shell commands on lifecycle events (start, crash, restart, exit):

json
{
  "exec": "node src/server.js",
  "events": {
    "start": "echo '🚀 starting'",
    "crash": "say 'Server crashed'",
    "restart": "curl -s http://localhost:3000/health > /dev/null && echo 'health-pinged'"
  }
}

Use crash to alert (desktop notification, Slack webhook) when a dev server dies; restart for warmup pings or cache prime.

Restart only on specific file events

bash
# Only restart on save (default), not on touch / mtime-only changes
nodemon --legacy-watch src/server.js

Output:

text
[nodemon] starting `node src/server.js`
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] using legacy watch

--legacy-watch falls back to polling — useful on network filesystems, WSL ↔ Windows bridges, and inside Docker volumes where inotify events don't propagate.

Production deployment

nodemon is dev-only. In production:

  • Use a real process supervisor (systemd, pm2, container orchestrator) for crash restart.
  • Use deploy-time builds; no runtime transpile.
  • Never run nodemon in a container destined for production — the file-watching overhead is wasted, and nodemon's restart-on-change is a security footgun if any deploy-time file is writeable.

The one valid production use of nodemon-style watching is staging environments where editors mount in a writeable volume for live-edit demos — but even there, pm2 --watch or node --watch is a better fit.

Performance tuning

nodemon's default settings are right for most cases. Knobs:

Narrow the watch scope

The biggest win is reducing the number of files watched. Default watch: "." traverses everything; restrict it:

json
{
  "watch": ["src"],
  "ignore": ["**/*.test.ts", "node_modules", "dist", ".git"]
}

A .gitignore is NOT used by default — explicit ignore patterns only.

Polling vs native watch

bash
nodemon --legacy-watch         # polling, ~1s interval
nodemon --polling-interval 500 # custom polling

Output:

text
[nodemon] starting `node src/server.js`
[nodemon] using polling

Polling burns CPU but works everywhere. Native (chokidar + inotify / FSEvents / ReadDirectoryChangesW) is faster and lower-power but flaky on virtual filesystems.

Limit extensions

bash
nodemon --ext js,ts --watch src

Output:

text
[nodemon] watching path(s): src
[nodemon] watching extensions: js,ts
[nodemon] starting `node src/server.js`

The default extension list (js,mjs,cjs,json) is fine; over-broad lists trigger restarts on irrelevant files (.md, .log).

Single-restart per save burst

--delay 500ms (above) is usually enough. For multi-file refactor saves (e.g. global rename), bump to --delay 2s.

Version migration guide

From → ToHighlights
1.x → 2.xSwitched to chokidar (cross-platform). --exec semantics standardised.
2.x → 3.xNode 10 dropped. Internal refactor of the CLI parser; some edge-case -- arg-passing fixed. nodemon.json schema unchanged.
3.x ongoingMinor releases tweak chokidar version and CLI parsing; no breaking changes planned.

Most projects can stay on whatever nodemon they have unless they're on a 1.x line — the upgrade story is trivial.

Security considerations

  1. Arbitrary command execution. nodemon --exec "$(cat /tmp/cmd)" runs whatever's in the command. Don't string-build --exec from user input; treat nodemon.json as code.
  2. events hook scripts. Same as above — events.restart: "rm -rf …" will happily destroy. Review events blocks like any shell script.
  3. Local LAN exposure. nodemon itself doesn't bind a port, but it relaunches your server. If that server binds 0.0.0.0, anyone on the LAN can hit your dev environment between restarts. Pair with host: "127.0.0.1".
  4. Inotify quota exhaustion. Watching huge directories (entire monorepos, node_modules) on Linux exhausts the per-user inotify watch limit. Symptom: nodemon silently misses events on later files. Raise /proc/sys/fs/inotify/max_user_watches or narrow the watch scope.
  5. Source-map exposure. If your --exec chain includes a TS transpiler, the same source-map concerns apply (paths leak into stack traces). Disable inline maps in CI builds.

Testing & CI integration

nodemon is rarely used in CI — there's nothing to "watch" in a one-shot CI run. The common CI use is under the dev-server probe in integration tests:

bash
# Spin up server in background under nodemon, run e2e, then kill
nodemon src/server.js &
SERVER_PID=$!
sleep 2
npx playwright test
kill $SERVER_PID

Output:

text
[nodemon] starting `node src/server.js`
server listening on http://localhost:3000
Running 8 tests using 4 workers
  8 passed (12.4s)

Better pattern: use concurrently or npm-run-all for "run server + tests" in parallel.

Ecosystem integrations

  • TypeScript — pair nodemon --exec tsx src/server.ts.
  • dotenvnodemon --exec "dotenv -- node src/server.js".
  • NestJSnest start --watch uses webpack under the hood, not nodemon. For non-nest projects, nodemon is still standard.
  • Express, Fastify, Koa — all happy under nodemon; restart on save is the default workflow.
  • GraphQL codegennodemon --watch schema.graphql --exec "graphql-codegen && tsx src/server.ts" regenerates types on schema change.
  • Storybook / Vite / Webpack dev servers — do NOT need nodemon; they have HMR built in.

Troubleshooting common errors

[nodemon] app crashed - waiting for file changes

The wrapped process exited with non-zero. nodemon prints this and waits — fix the bug in your code, save, and nodemon resumes. The error from your code is still visible in stdout above the crash message.

Watcher doesn't pick up changes (WSL / Docker / NFS)

Native filesystem events don't propagate. Switch to polling:

bash
nodemon --legacy-watch src/server.js

Output:

text
[nodemon] starting `node src/server.js`
[nodemon] using legacy watch

EMFILE: too many open files

The watcher hit the OS file-descriptor limit. Either raise ulimit -n or narrow the watch path.

ENOSPC: System limit for number of file watchers reached

Linux inotify limit. Raise it:

bash
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Output:

text
fs.inotify.max_user_watches = 524288

Restarts are too aggressive

Add --delay 1s and tighten the watch path. Editors that "save as swap then rename" can fire multiple events per save.

nodemon doesn't pass --inspect through

nodemon --inspect src/server.js puts --inspect BEFORE the script name — that flag goes to nodemon, not Node. Use the --exec form:

bash
nodemon --exec "node --inspect src/server.js"

Output:

text
[nodemon] starting `node --inspect src/server.js`
Debugger listening on ws://127.0.0.1:9229/...
For help, see: https://nodejs.org/en/docs/inspector

When NOT to use this

  • Production processes. Use systemd, pm2, or a container orchestrator's restart policy.
  • Frontend dev servers. Vite / Webpack / Next ship HMR — file-watch is built in and smarter than nodemon's restart-everything.
  • Simple Node servers on Node 18.11+. node --watch src/server.js is enough if you don't need exec hooks or fancy globbing.
  • TS-only dev with imports tracked statically. tsx watch follows the import graph — faster and quieter than nodemon globs.
  • Polyglot pipelines. watchexec (Rust) handles arbitrary commands with better defaults across languages.

See also