Choosing nodemon file watching: native fs.watch vs legacy polling
Decide between native fs.watch and legacy polling in nodemon. Compare CPU overhead and reliability for local dev versus Docker or NFS environments.
20 Oct 2025, 17:58 UTC

Decision: native watch or polling for nodemon
The primary problem in Node.js development environments is the "silent failure" of restarts. When nodemon fails to detect a file change, it is usually because the underlying filesystem does not support the OS-level events that nodemon expects. The decision is whether to rely on the native fs.watch API or force a legacy polling loop.
Native watching is efficient but can fail on Docker volumes, Vagrant shared folders, or Network File Systems (NFS). Polling is reliable across all filesystems but consumes more CPU. Because the default behavior changed between nodemon 1.x (polling) and 2.x (native), you must first verify your version to understand your current baseline.
# Run in project root terminal
nodemon -v
This command requires Node.js to be installed. No elevated permissions are needed. The risk of ignoring this step is assuming a default behavior that does not match your installed version.
Supported watching options
| Option | How to enable | Default (nodemon ≥2.0) | Typical use case |
|---|---|---|---|
| Native fs.watch | Omit --legacy-watch or set "legacyWatch": false in nodemon.json |
true | Local development on Linux, macOS, or Windows native disks |
| Legacy polling | nodemon --legacy-watch or "legacyWatch": true |
false | Docker bind mounts, Vagrant, NFS, or CI containers |
| Hybrid (auto-fallback) | Default behavior | true (auto) | Environments that occasionally hit OS watch descriptor limits |
Trade-offs and Constraints
Native fs.watch provides the lowest possible CPU overhead and near-instantaneous event delivery. It leverages the operating system's own notification system, meaning the process remains idle until the OS signals a change. However, it is prone to silent failures in virtualized environments where the host OS does not propagate file events to the guest OS.
Legacy polling guarantees detection because nodemon manually checks the stats of watched files at a regular interval (defaulting to 100ms). The trade-off is a steady increase in CPU usage—typically 1–3% of a core per watched file—and a slight delay in restart latency proportional to the polling interval.
Hybrid mode attempts to use fs.watch first. If the OS returns an EMFILE error (too many open files), nodemon automatically switches to polling. While this prevents the process from crashing, it does not solve the problem of missed events on network drives.
Limitation: Polling a large directory tree with a very short interval can significantly degrade system performance. Only adjust the interval after measuring CPU impact.
Concrete Configuration
To make the watching strategy explicit and consistent across a team, use a nodemon.json file in the project root.
{
"legacyWatch": false,
"ignore": ["src/**/*.test.js", "node_modules", ".git"],
"exec": "node ./src/index.js"
}
If you are working within a Docker container and notice that changes to your code are not triggering restarts, switch to polling by updating the config to "legacyWatch": true or by using the CLI flag:
# Run in project root terminal
nodemon --legacy-watch node ./src/index.js
To further tune performance in polling mode, you can specify a custom interval (e.g., 250ms):
nodemon --legacy-watch --interval 250 node ./src/index.js
Risk: Increasing the interval reduces CPU load but increases the time between saving a file and the app restarting.
Validation and Verification
- Functional Check: Start nodemon with your chosen configuration. Edit a file that is not in the
ignorelist. Verify that the console displays a restart message. For native watch, this should be nearly instant; for polling, it should occur within the specified interval. - Resource Check: Open a system monitor (e.g.,
top,pidstat, or Windows Task Manager). Compare the CPU percentage of the Node process while idle. Native watch should show near-zero usage, while polling will show a consistent, low-level CPU consumption. - Environment Simulation: If using Docker, mount a volume with the
:cachedflag. Test withlegacyWatch: false; if restarts fail, switch tolegacyWatch: trueto verify that polling resolves the event loss.
Rollback: To revert these changes, remove the --legacy-watch flag from your start script or set "legacyWatch": false in nodemon.json and restart the process.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.