Skip to content
Node.js nd libraries 5 min read

nodemon: Auto-Restart in Development

Restarting your server by hand after every edit kills momentum. nodemon watches your project files and automatically restarts the Node process the moment something changes, so the edit-save-test loop becomes nearly instant. It is a development-only tool — you never ship it to production — but for local work it is one of the highest-leverage utilities in the Node ecosystem. Modern Node also has a built-in --watch flag that covers many of the same cases with zero dependencies.

Installing and running nodemon

nodemon belongs in devDependencies because it is only needed while you are coding. Install it locally rather than globally so every contributor gets the same version:

npm install --save-dev nodemon

Swap your usual node server.js for nodemon server.js. By default it watches the current directory for changes to .js, .mjs, .cjs, .json, and a few other extensions, and restarts when any of them change:

npx nodemon server.js

Output:

[nodemon] 3.1.10
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node server.js`
Listening on http://localhost:3000
[nodemon] restarting due to changes...
[nodemon] starting `node server.js`
Listening on http://localhost:3000

Typing rs then pressing Enter forces a manual restart without touching a file — useful when a change happens outside the watched tree. Wire nodemon into an npm script so nobody has to remember the flags:

{
  "scripts": {
    "dev": "nodemon server.js",
    "start": "node server.js"
  }
}

Now npm run dev gives you auto-restart, while npm start runs plain Node for production.

Configuration with nodemon.json

Passing flags on the command line gets unwieldy fast. Instead, drop a nodemon.json file in the project root and nodemon reads it automatically. This keeps the configuration in version control and consistent across the team:

{
  "watch": ["src", "config"],
  "ext": "js,json,ts",
  "ignore": ["src/**/*.test.js", "dist", "logs"],
  "exec": "node --env-file=.env src/server.js",
  "delay": 500,
  "env": {
    "NODE_ENV": "development"
  }
}

You can alternatively nest the same keys under a nodemonConfig block in package.json if you prefer a single config file. The most useful options are:

OptionTypePurpose
watchstring[]Directories or files to monitor for changes
ignorestring[]Glob patterns to exclude (tests, build output, logs)
extstringComma-separated extensions that trigger a restart
execstringCommand to run, e.g. for TypeScript or custom flags
delaynumberMilliseconds to debounce before restarting
envobjectExtra environment variables for the child process
eventsobjectShell commands to run on lifecycle events (start, restart, crash)

nodemon does not crash when your app does. If your process exits with an error it prints app crashed - waiting for file changes before starting... and sits idle until you save again. This is intentional — it lets you read the stack trace instead of having the terminal scroll away.

Watch and ignore patterns

Watching too much makes restarts noisy and slow; watching too little means changes get missed. Be explicit. The watch array narrows monitoring to the directories that actually contain source, and ignore removes the rest:

{
  "watch": ["src"],
  "ignore": ["**/*.test.js", "**/*.spec.js", "public/**", "*.md"]
}

By default nodemon already ignores node_modules, .git, and dotfiles, so you rarely need to list those. Patterns use standard globbing — ** matches any depth, * matches within a path segment. You can express the same intent on the command line for one-off runs:

npx nodemon --watch src --ext js,json --ignore "src/**/*.test.js" src/server.js

For non-JavaScript workflows, point exec at the right runner. To develop a TypeScript server without a separate build step, combine nodemon with tsx:

{
  "watch": ["src"],
  "ext": "ts",
  "exec": "tsx src/server.ts"
}

Node’s built-in --watch flag

Since Node.js 18.11 (stable from 20 onward) the runtime ships its own watch mode, so for many projects you can drop the nodemon dependency entirely. Pass --watch and Node restarts the process when any imported file changes:

node --watch server.js

Output:

Listening on http://localhost:3000
Completed running 'server.js'. Waiting for file changes before restarting...
Restarting 'server.js'
Listening on http://localhost:3000

The built-in watcher is smart: it tracks the actual module graph rather than a directory, so it only restarts on files your app really uses. Scope it with --watch-path and exclude paths via the dedicated flag. It also pairs naturally with the native env-file loader:

node --watch-path=./src --env-file=.env src/server.js

A common all-native dev script needs no third-party tooling at all:

{
  "scripts": {
    "dev": "node --watch --env-file=.env src/server.js"
  }
}

The two approaches differ in scope and flexibility:

Capabilitynodemonnode --watch
Extra dependencyYes (dev only)No
Watch by module graphNo (by directory)Yes
Custom exec / runnersYesNo
Lifecycle event hooksYesNo
Manual rs restartYesNo
Config filenodemon.jsonFlags only

Use node --watch when your project is plain Node and you want zero extra tooling. Reach for nodemon when you need custom run commands (TypeScript, transpilers), debounce control, lifecycle hooks, or fine-grained ignore rules.

Best Practices

  • Keep nodemon in devDependencies and run production with plain node — never ship a watcher to a live environment.
  • Drive it through an npm dev script so flags and config stay consistent across the team.
  • Scope watch to your source directories and ignore test files, build output, and logs to avoid restart storms.
  • Use nodemon.json (or nodemonConfig in package.json) instead of long command lines so configuration lives in version control.
  • Set a small delay when an editor or build tool writes many files at once, to debounce noisy restarts.
  • Prefer Node’s built-in --watch for simple JavaScript projects to eliminate a dependency entirely.
  • Pair watch mode with --env-file so your dev process loads configuration the same way every time.
Last updated June 14, 2026
Was this helpful?