Fix: ENOSPC: System limit for number of file watchers reached

The Error

You start a development server, run a build tool, or open a project in VS Code, and the process crashes with this error:

Error: ENOSPC: System limit for number of file watchers reached, watch '/home/user/project/src'

You may also see variations depending on the tool:

Error from chokidar (/home/user/project/src): Error: ENOSPC: System limit for number of file watchers reached, watch '/home/user/project/src'

In webpack or Create React App:

[webpack-dev-server] Error: ENOSPC: System limit for number of file watchers reached
    at FSWatcher._handle.onchange (node:internal/fs/watchers:207:21)

In VS Code, you might see a notification:

Visual Studio Code is unable to watch for file changes in this large workspace.
Please follow the instructions link to resolve this issue.

Or when running Jest:

ENOSPC: System limit for number of file watchers reached, watch '/home/user/project/node_modules'

This error means your operating system has hit the maximum number of files it can monitor for changes at the same time. Every tool that watches files for hot reloading, live compilation, or file syncing uses a kernel feature called inotify, and that feature has a hard cap.

Why This Happens

On Linux, the kernel uses inotify to notify applications when files change. Each file or directory being watched consumes one inotify watch. The system has a configurable limit on how many watches a single user can create, controlled by the kernel parameter fs.inotify.max_user_watches.

The default value on most Linux distributions is 8192 watches. That sounds like a lot, but modern JavaScript projects can easily exceed it. A single node_modules directory can contain tens of thousands of files and subdirectories. When you run a development server that watches your project for changes, it often recursively watches every file and directory under your project root — including node_modules.

Stack multiple projects, add VS Code (which also watches files), a running test runner, and maybe a Docker container with its own watchers, and you blow past 8192 in seconds.

The math is simple: if you have 30,000 files in node_modules, 2,000 files in your source tree, and VS Code is watching all of them, you need 32,000+ watches. The default limit of 8192 is not enough. This is similar in nature to running into permission issues on Linux where a system-level configuration blocks what should be a routine development task.

Fix 1: Increase the Watcher Limit Temporarily

The fastest fix is to increase the limit for the current session using sysctl. This change will be lost when you reboot.

sudo sysctl fs.inotify.max_user_watches=524288

This sets the limit to 524,288, which is more than enough for most development workflows. You can verify it took effect:

cat /proc/sys/fs/inotify/max_user_watches

You should see 524288. Now restart your dev server or VS Code, and the error should be gone.

If you also hit a limit on inotify instances (less common, but possible when running many tools simultaneously), increase that too:

sudo sysctl fs.inotify.max_user_instances=1024

Fix 2: Increase the Watcher Limit Permanently

The temporary fix resets on reboot. To make it permanent, write the setting to the sysctl configuration file.

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

Then apply the changes without rebooting:

sudo sysctl -p

On some distributions using systemd, the preferred location is a dedicated file under /etc/sysctl.d/:

echo "fs.inotify.max_user_watches=524288" | sudo tee /etc/sysctl.d/50-file-watchers.conf
sudo sysctl --system

Both approaches work. The /etc/sysctl.d/ method is cleaner because it keeps custom settings separate from the base sysctl.conf file.

Verify the change persists:

cat /proc/sys/fs/inotify/max_user_watches

After a reboot, check again to make sure the setting survived. If it didn’t, make sure no other configuration file is overriding your value. Files in /etc/sysctl.d/ are read in alphabetical order, and a later file can overwrite an earlier one.

Fix 3: Check Your Current Inotify Usage

Before blindly increasing the limit, it helps to understand what is consuming your watches. You can see the current total number of watches in use:

find /proc/*/fdinfo -type f 2>/dev/null | xargs grep -c inotify 2>/dev/null | grep -v ':0$'

To see which processes are using the most inotify watches:

for pid in $(find /proc/*/fd -lname 'anon_inode:inotify' 2>/dev/null | cut -d/ -f3 | sort -u); do
  count=$(grep -c inotify /proc/$pid/fdinfo/* 2>/dev/null)
  cmd=$(cat /proc/$pid/cmdline 2>/dev/null | tr '\0' ' ')
  echo "$count $pid $cmd"
done | sort -rn | head -20

This shows you the top 20 processes by inotify watch count. You might discover that a forgotten background process — a stale dev server, an old nodemon instance, or a file sync tool — is hoarding watches. Killing those processes frees up watches immediately without changing any system settings.

You can also check the current limit and how close you are to hitting it:

# Current limit
cat /proc/sys/fs/inotify/max_user_watches

# Current usage (approximate)
find /proc/*/fdinfo -type f -exec grep -l inotify {} \; 2>/dev/null | wc -l

Fix 4: Configure VS Code File Watching

VS Code watches your entire workspace for changes to provide features like auto-import, file explorer updates, and Git integration. In large projects, this alone can consume thousands of watches.

You can tell VS Code to exclude directories from watching by adding a files.watcherExclude setting in your settings.json:

{
  "files.watcherExclude": {
    "**/node_modules/**": true,
    "**/.git/objects/**": true,
    "**/.git/subtree-cache/**": true,
    "**/dist/**": true,
    "**/build/**": true,
    "**/coverage/**": true,
    "**/.next/**": true,
    "**/tmp/**": true
  }
}

VS Code already excludes node_modules and .git objects by default, but explicitly listing them ensures they are always excluded. Add any build output directories, cache directories, or other large directories you don’t need VS Code to watch.

You can also reduce the search scope by configuring files.exclude and search.exclude:

{
  "files.exclude": {
    "**/node_modules": true,
    "**/dist": true
  },
  "search.exclude": {
    "**/node_modules": true,
    "**/dist": true,
    "**/coverage": true,
    "**/package-lock.json": true
  }
}

These settings prevent VS Code from indexing or searching those directories, which reduces memory usage and watch consumption. If VS Code still struggles with your workspace, check that you do not have module resolution issues that cause the editor to scan unnecessary directories.

Fix 5: Ignore node_modules in Your File Watcher

Many build tools and dev servers watch node_modules by default, which is almost never what you want. You can configure your tools to ignore it.

webpack (via watchOptions):

// webpack.config.js
module.exports = {
  watchOptions: {
    ignored: /node_modules/,
  },
};

nodemon:

{
  "ignore": ["node_modules", "dist", "build", ".git"]
}

Save this as nodemon.json in your project root, or pass the flag directly:

nodemon --ignore node_modules/ --ignore dist/ server.js

Vite:

Vite already ignores node_modules by default, but if you have custom watch targets, make sure they are scoped tightly:

// vite.config.js
export default {
  server: {
    watch: {
      ignored: ['**/node_modules/**', '**/dist/**'],
    },
  },
};

chokidar (used by many tools internally):

const chokidar = require('chokidar');

const watcher = chokidar.watch('./src', {
  ignored: /(^|[\/\\])\../, // ignore dotfiles
  persistent: true,
  ignoreInitial: true,
});

By restricting the watcher to ./src instead of ., you avoid watching node_modules, dist, and other top-level directories entirely. If you run into issues where Node.js cannot resolve your modules after making these changes, see how to fix Cannot find module errors.

Fix 6: Fix for Docker and WSL Environments

Running a dev server inside Docker or Windows Subsystem for Linux (WSL) introduces unique file watching problems.

Docker with bind mounts:

When you mount a host directory into a Docker container with -v, inotify events from the host do not propagate into the container on some setups. The container’s file watcher never sees changes. Common workarounds:

  1. Use polling instead of inotify. Most tools support a polling fallback:
# webpack
CHOKIDAR_USEPOLLING=true npm start

# or in webpack config
module.exports = {
  watchOptions: {
    poll: 1000, // Check for changes every second
  },
};
# nodemon
nodemon --legacy-watch server.js
  1. Increase the inotify limit inside the container. Docker containers share the host kernel’s inotify limit by default, but you may need to set it explicitly:
# docker-compose.yml
services:
  app:
    build: .
    volumes:
      - .:/app
    # Required for inotify to work
    privileged: true

Or set the sysctl on the host, since containers inherit the host kernel’s limits.

  1. Use Docker volumes instead of bind mounts for node_modules:
services:
  app:
    volumes:
      - .:/app
      - /app/node_modules  # Anonymous volume — not watched from host

This prevents the host’s node_modules from being mounted into the container, which reduces the number of files the watcher needs to track. This is also a good practice for avoiding Docker permission errors caused by UID mismatches between host and container.

WSL 2:

WSL 2 runs a real Linux kernel, so inotify works natively — but only for files stored on the Linux filesystem. Files on the Windows filesystem (mounted under /mnt/c/) do not generate inotify events reliably.

If your project is at /mnt/c/Users/you/project, move it to the Linux filesystem:

# Move project to Linux filesystem
cp -r /mnt/c/Users/you/project ~/project
cd ~/project
npm install

Performance will improve dramatically. File watching will work without polling, and npm install will be significantly faster because the Linux ext4 filesystem handles many small files far better than the Windows NTFS filesystem accessed through the 9P protocol.

If you must keep the project on the Windows filesystem, enable polling:

CHOKIDAR_USEPOLLING=true npm start

Fix 7: Reduce the Number of Watched Files

Sometimes the best fix is to simply watch fewer files. If your project has grown large, audit what is actually being watched.

Clean up unused dependencies:

# Remove packages not listed in package.json
npm prune

# Find unused dependencies
npx depcheck

Fewer packages in node_modules means fewer files to watch. If your node_modules is bloated and you suspect broken installations, see how to fix Node.js module resolution errors for cleanup steps.

Use .gitignore-aware watching:

Many tools respect .gitignore by default. Make sure your .gitignore includes:

node_modules/
dist/
build/
coverage/
.next/
.cache/
*.log
tmp/

Split large monorepos:

If you have a monorepo with dozens of packages, consider configuring your tools to only watch the packages you are actively developing, not the entire tree:

# Instead of watching everything
npm run dev

# Watch only the specific package
npm run dev --workspace=packages/my-app

Delete stale projects:

If you have multiple old projects open in VS Code or running dev servers in the background, each one consumes watches. Close tabs, stop servers, and kill orphaned processes:

# Find and kill orphaned node processes
ps aux | grep node
kill <pid>

# Or kill all node processes (use with caution)
killall node

Fix 8: Use Alternative Watch Mechanisms

If increasing the inotify limit is not possible (restricted systems, shared hosting, CI environments), you can switch to alternative watch mechanisms.

Polling mode:

Most file watchers support a polling fallback that does not use inotify at all. It periodically stats files to detect changes. It is slower and uses more CPU, but it works regardless of the inotify limit.

# Environment variable used by chokidar (and tools that use it)
export CHOKIDAR_USEPOLLING=true
export CHOKIDAR_INTERVAL=3000  # Poll every 3 seconds to reduce CPU usage

For webpack:

module.exports = {
  watchOptions: {
    poll: 2000,
    aggregateTimeout: 500,
    ignored: /node_modules/,
  },
};

The aggregateTimeout setting delays the rebuild after the first change is detected, which batches rapid changes into a single rebuild.

Use watchman as an alternative backend:

Facebook’s Watchman is a more efficient file watching service that handles large projects better than raw inotify. Jest and Metro (React Native bundler) both support Watchman natively.

# Install watchman
sudo apt-get install watchman

# Or on Fedora/RHEL
sudo dnf install watchman

Once installed, Jest will automatically use Watchman instead of inotify-based watching. Watchman manages its own watch state efficiently and can handle millions of files without hitting system limits.

Disable watch mode entirely in CI:

In CI/CD pipelines, you do not need file watching at all. Run your builds and tests in single-run mode:

# Jest
jest --watchAll=false

# React Scripts
CI=true npm test

# webpack
webpack --mode production  # No watch

This avoids the ENOSPC error entirely in environments where you have no control over system settings. If your CI builds fail with module resolution errors alongside the watcher error, fix the module issue separately — it is unrelated.

Still Not Working?

The Limit Is Set but the Error Persists

If you’ve increased max_user_watches and confirmed the new value with cat /proc/sys/fs/inotify/max_user_watches, but still get the error, check max_user_instances:

cat /proc/sys/fs/inotify/max_user_instances

The default is often 128. Each inotify instance (not watch) corresponds to one inotify_init() call. A single Node.js process might create multiple instances. Increase it:

echo "fs.inotify.max_user_instances=1024" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

The Error Returns After Reboot

Your sysctl setting might be overridden. Check for conflicting files:

sudo sysctl --system 2>&1 | grep max_user_watches

This shows all files that set the value. The last one wins. If a system package drops a file in /etc/sysctl.d/ that sets a lower value with a higher alphabetical name (like 99-defaults.conf), it will override your setting. Rename your file to sort after it, or edit the conflicting file directly.

You Are Running Multiple Linux Users

The max_user_watches limit is per user. If you run your dev server as one user and VS Code as another (or root), each user has their own limit. Make sure the limit is high enough for the user that is actually hitting it.

systemd Services Are Consuming Watches

Some systemd services (like systemd-journald, snapd, or custom services) use inotify watches. If your system is watch-heavy even without development tools running, audit system-level consumption using the diagnostic script from Fix 3.

Network Filesystems

inotify does not work on NFS, CIFS/SMB, or other network filesystems. If your project is on a network share, file watching will silently fail or throw errors. Use polling mode, or move the project to a local filesystem. This same class of issue can cause unexpected behavior in other areas too, similar to how React hydration mismatches can silently cause problems that only surface later.

Containerized Development Environments (Dev Containers, Codespaces, Gitpod)

Remote development environments often have restrictive inotify limits that you cannot change. In these cases:

  1. Use polling mode as described in Fix 8.
  2. Scope your watchers tightly to only src/ or your source directories.
  3. Use the workspace’s built-in terminal settings to set CHOKIDAR_USEPOLLING=true in your shell profile.

Related: Fix: Cannot find module | Fix: bash permission denied | Fix: docker permission denied | Fix: Module not found: Can’t resolve | Fix: Too many re-renders

Related Articles