Skip to content

fix: Reduce inotify watch count via gitignore-aware directory walking on Linux#12398

Open
antoinelyset wants to merge 1 commit intovercel:mainfrom
antoinelyset:fix/gitignore-aware-inotify-watches
Open

fix: Reduce inotify watch count via gitignore-aware directory walking on Linux#12398
antoinelyset wants to merge 1 commit intovercel:mainfrom
antoinelyset:fix/gitignore-aware-inotify-watches

Conversation

@antoinelyset
Copy link

@antoinelyset antoinelyset commented Mar 19, 2026

Summary

Hello Turborepo! I ran into a limitation of watch mode on Linux. In our monorepo, node_modules creates enough directories to exceed the OS inotify limit (e.g. GKE default of 12,288), causing turbo watch to fail with ERROR file watcher failed to start.

I gave it a try with the help of Claude Code — I didn't let it code blindly, I guided it carefully, used TDD, and built a Docker-based reproduction to verify the fix end-to-end.

Known limitation — looking for guidance

In the end it's not done because I discovered that using .gitignore to filter directories might be too broad.

Skipping all gitignored directories means turbo loses inotify watches on output directories like out/ and dist/ — which are typically gitignored but still need to be watched. hash_watcher relies on inotify events to detect when output files change and recompute hashes. Without those watches, hashes go stale. This causes 8 test failures on Linux:

  • 6 globwatcher tests (timeout — no events from gitignored output dirs)
  • 2 hash_watcher tests (test_negative_inputs, test_inputs_with_turbo_defaults — hashes not updated because modification events from dist//out/ never arrive)

Possible approaches — would love your input:

  1. Hardcode node_modules skip — Only skip node_modules (and .git) instead of relying on .gitignore.
  2. Gitignore-based skip + re-add turbo output dirs — Use .gitignore for the initial skip, then add watches back for directories that match task outputs from turbo.json.
  3. Custom turbo ignore list — A turbo-specific config for which directories to skip during inotify setup, independent of .gitignore.
  4. Something else? — Happy to hear what approach fits best with turbo's architecture.

What this does

Replaces WalkDir with ignore::WalkBuilder (.git_ignore(true)) in the Linux-only manually_add_recursive_watches(), so gitignored directories are skipped during inotify watch registration.

Current Design choices

  • Registration-time only: Filters at inotify setup, not at event delivery — all events still reach subscribers like hash_watcher unchanged.
  • Linux-only: macOS/Windows use tree-level watchers without per-directory limits, so they're unaffected.
  • Cookie dir safety: ensure_watched_up_to_root() ensures .turbo/cookies gets watches even when .turbo is gitignored.

Reproduction

Scenario inotify limit Result
Stock turbo 2.8.19 100 ERROR file watcher failed to start
Patched turbo 500 Starts successfully

Repro repo: antoinelyset/turbo-inotify-repro

@antoinelyset antoinelyset requested a review from a team as a code owner March 19, 2026 21:48
@antoinelyset antoinelyset requested review from tknickman and removed request for a team March 19, 2026 21:48
@vercel
Copy link
Contributor

vercel bot commented Mar 19, 2026

@antoinelyset is attempting to deploy a commit to the Vercel Team on Vercel.

A member of the Team first needs to authorize it.

.git_ignore(true)
.git_global(false) // skip ~/.gitignore — not predictable in CI
.follow_links(false)
.filter_entry(|e| e.file_name() != OsStr::new(".git"))
Copy link
Contributor

@vercel vercel bot Mar 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WalkBuilder defaults require_git to true, causing .gitignore files to be silently ignored when no .git directory is found in the walk root or ancestors.

Fix on Vercel

… on Linux

On Linux, turbo's file watcher creates one inotify watch per directory
using recursive directory walking. In monorepos with large node_modules,
this easily exceeds the OS inotify limit (e.g. GKE default of 12,288),
causing `turbo watch` to fail with "file watcher failed to start".

Replace `walkdir::WalkDir` with `ignore::WalkBuilder` in the Linux-only
`manually_add_recursive_watches()` so that gitignored directories
(primarily node_modules) are skipped during inotify watch setup. This
reduces the watch count from ~13k+ to just the project's own directories.

Also adds `ensure_watched_up_to_root()` to guarantee the cookie directory
(.turbo/cookies) gets inotify watches even when .turbo is gitignored.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@antoinelyset antoinelyset force-pushed the fix/gitignore-aware-inotify-watches branch from 1a7024f to 6354e63 Compare March 19, 2026 22:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant