How do you enable, disable, and diagnose File System Watching in a Gradle build?
answer
- org.gradle.vfs.watch true/false
- --watch-fs / --no-watch-fs
- org.gradle.vfs.verbose retained vs invalidated
- default true on supported OS (7+)
- CI: daemon not reused → little benefit
basics
~10 sSet org.gradle.vfs.watch=true (or false) in gradle.properties, or use --watch-fs / --no-watch-fs per invocation. Add org.gradle.vfs.verbose=true to log retained vs. invalidated paths.
solid answer
~40 sWatching is controlled by the **`org.gradle.vfs.watch`** boolean property, set in `gradle.properties` (project or `~/.gradle`). It defaults to **`true`** on supported OSes since Gradle 7.0. To override per invocation use the command-line flags **`--watch-fs`** and **`--no-watch-fs`**. For diagnostics, set **`org.gradle.vfs.verbose=true`**, which prints, at the end of each build, how many VFS paths were **retained** (trusted from the prior build) versus **invalidated** (re-snapshotted). Because retention lives in the **daemon**, watching only helps across builds when the daemon is reused — a verbose log showing all-invalidated repeatedly suggests the daemon is being restarted, an unsupported filesystem, or a low inotify limit. You typically enable it everywhere for local development; on CI it's often less useful because daemons aren't reused between jobs.
code
bash · 5 lines# Per-invocation overrides
./gradlew build --watch-fs # force on
./gradlew build --no-watch-fs # force off
# Diagnose retention
./gradlew build -Dorg.gradle.vfs.verbose=truego deeper
Know the property and the two command-line flags, and the verbose toggle.
Interpret retained-vs-invalidated output and relate it to daemon reuse.
Diagnose zero-retention cases and decide local-vs-CI configuration.
Set org-wide defaults in init scripts / gradle.properties and codify the CI stance.
## The controls | Mechanism | Where | Effect | |---|---|---| | `org.gradle.vfs.watch=true/false` | `gradle.properties` (project or `~/.gradle/`) | Master switch; default `true` on supported OSes (Gradle 7+) | | `--watch-fs` / `--no-watch-fs` | command line | Per-invocation override of the property | | `org.gradle.vfs.verbose=true` | `gradle.properties` or `-D` | Logs retained vs. invalidated path counts each build | ```properties # gradle.properties org.gradle.vfs.watch=true org.gradle.vfs.verbose=true ``` ```bash # force-disable for one run ./gradlew build --no-watch-fs # force-enable for one run ./gradlew build --watch-fs ``` ## Reading the verbose output With verbose enabled Gradle prints something like 'Received N file system events... Virtual file system retained information about X files since last build'. A healthy warm build shows a **high retained / low invalidated** ratio. If retained is consistently ~0: - The **daemon was restarted** (e.g. different JVM args, `--no-daemon`, or it was killed) — there's nothing to retain. - The workspace is on an **unsupported filesystem** (NFS, some container mounts) so events aren't delivered. - On Linux the **inotify quota** was exceeded and watching was disabled with a warning. ## When to enable - **Local development:** leave it on (the default). It's where the same daemon serves many incremental builds, so retention pays off. - **CI:** the daemon usually isn't reused between jobs (fresh agents, `--no-daemon`, or one build per container), so cross-build retention rarely applies. It's harmless to leave default, but don't expect a win, and on constrained Linux agents the inotify limit may force a warning. Some teams set `--no-watch-fs` on CI to avoid the warning noise. ## Interaction with the daemon Watching and the daemon are coupled: the VFS is daemon-resident. If you intentionally run `--no-daemon`, watching provides no cross-build benefit even when enabled. Conversely, anything that fragments daemon reuse (varying `org.gradle.jvmargs`, frequent daemon expiry) also defeats retention.
- Verbose logs show 'retained 0 files' on every local build despite watch=true. What would you check?Whether the daemon is actually being reused (consistent JVM args, not --no-daemon, daemon not expiring), the filesystem type (local vs NFS/container), and on Linux the inotify quota. All-invalidated means no live daemon held the prior VFS or events aren't delivered.
- Is it worth enabling watching on CI?Usually not for cross-build retention, because CI agents rarely reuse a daemon between jobs. It's harmless but provides little benefit; on constrained Linux agents teams sometimes disable it to avoid inotify warnings.
saying these in an interview costs you the question
- Confusing org.gradle.vfs.watch with the build cache (org.gradle.caching) — they are different optimizations.
- Believing verbose mode changes performance — it only adds logging.