inotifywait: Watching Filesystem Events
What is inotifywait?
Conclusion:
inotifywaituses the Linux inotify kernel feature to detect file create/modify/delete events without polling, so you can trigger automation the moment a file changes.
- Detect filesystem events in real time with
inotifywait - Combine it with a
while readloop to build a change-detect → auto-process pattern - Avoid the two pitfalls:
close_writeandmax_user_watches
Quick Summary
- Wait once →
inotifywait path - Monitor continuously and process events →
inotifywait -m -e close_write path - To catch "save finished," use
close_write, notmodify
Assumptions (environment)
- OS: Linux (Ubuntu / Debian / RHEL family), inotify on kernel 2.6.13+
- The
inotify-toolspackage is required (see below) - Local filesystem assumed (some network filesystems like NFS cannot report events)
Why use inotifywait? (vs polling)
Conclusion: Polling with
while sleep 1; do ...; donewastes CPU and reacts late. inotifywait lets the kernel push events, so it stays idle-cheap and reacts instantly.
The naive way to detect changes is to repeatedly run ls or stat — polling. That has downsides:
- A short interval wastes CPU and I/O
- A long interval delays detection
- Scan cost grows with the number of watched files
inotifywait registers events with the kernel's inotify subsystem and only wakes up when a change happens. Idle load is near zero and the reaction is immediate. Where the watch command re-renders on a fixed interval (polling), inotifywait is event-driven.
How do you install inotifywait?
Conclusion:
inotifywaitis not part of coreutils — it ships in theinotify-toolspackage. Install it withapton Debian-based systems ordnfon RHEL-based ones.
# Ubuntu / Debian $ sudo apt install inotify-tools # RHEL / Rocky / AlmaLinux (may require EPEL) $ sudo dnf install inotify-tools # Fedora $ sudo dnf install inotify-tools
Verify the install:
$ inotifywait --help | head -n 1
inotifywait 3.22.6.0
The bundled inotifywatch tool aggregates event statistics ("which files were accessed how often"). This article focuses on inotifywait, which streams events one by one for processing.
Basic usage: watching for file changes
Conclusion: Pass a path and inotifywait blocks until one event occurs, prints it, and exits. Add
-m(monitor) to keep watching continuously.
Wait once (one-shot)
$ inotifywait /tmp/watchdir
Touch a file in /tmp/watchdir from another terminal and it prints one event, then exits:
Setting up watches. Watches established. /tmp/watchdir/ MODIFY test.txt
The output order is watched-path event-name filename. By default it watches all event types and exits on the first one.
Monitor continuously (-m)
Exiting every time is useless for automation. With -m (--monitor) it never exits and keeps emitting events:
$ inotifywait -m /tmp/watchdir
Setting up watches. Watches established. /tmp/watchdir/ OPEN test.txt /tmp/watchdir/ MODIFY test.txt /tmp/watchdir/ CLOSE_WRITE,CLOSE test.txt
Startup messages like Setting up watches. go to standard error. Suppress them with -q (quiet) when scripting.
Which events should you watch? (choosing -e)
Conclusion: Use
-eto narrow the events. To catch "file save complete," useclose_write, notmodify—modifyfires on every write.
Without -e (--event), every event type is in scope, which is noisy. Common events:
| Event | Fires when |
|---|---|
create |
A file / directory was created |
modify |
Contents were written (fires multiple times) |
close_write |
A file opened for writing was closed |
delete |
A file / directory was deleted |
moved_to |
A file was moved / renamed into this directory |
moved_from |
A file was moved / renamed out of this directory |
attrib |
Permissions, owner, or timestamps changed |
Specify several events by repeating -e or comma-separating them.
# Watch only create, write-complete, and delete $ inotifywait -m -e create -e close_write -e delete /tmp/watchdir
Why close_write instead of modify
Editors and programs often write multiple times for a single save, and modify fires each time. To catch the moment a save finishes exactly once, use close_write, which means the file was closed. Miss this and you get "the same file processed over and over."
Formatting output: --format and --timefmt
Conclusion:
--formatreshapes the output. The basics are%wwatched path,%ffilename,%eevent name, and%Ttimestamp (used with--timefmt).
The default output is space-separated and awkward to parse. Use --format to produce something script-friendly.
$ inotifywait -m --timefmt '%F %T' --format '%T | %e | %w%f' \
-e close_write /tmp/watchdir2026-06-05 21:30:11 | CLOSE_WRITE,CLOSE | /tmp/watchdir/report.csv
Key format specifiers:
%w… the watched path (the directory being watched)%f… the filename the event applies to (when watching a directory)%e… the events that occurred (comma-separated)%T… timestamp, formatted by the--timefmtstrftime string
Joining %w%f gives the full path. To change the separator, use %Xe, where X is the separator character.
Watching directories recursively (-r and watch limits)
Conclusion:
-rwatches subdirectories recursively, but each directory consumes one watch, and hitting thefs.inotify.max_user_watcheslimit makes it fail.
$ inotifywait -m -r -e close_write /var/www
Watching a huge tree with -r can produce this error:
Failed to watch /var/www; upper limit on inotify watches reached! Please increase the amount of inotify watches allowed per user via `/proc/sys/fs/inotify/max_user_watches'.
Check the current limit:
$ cat /proc/sys/fs/inotify/max_user_watches
8192
Raise it temporarily (resets on reboot):
$ sudo sysctl fs.inotify.max_user_watches=524288
Make it persistent with a config file:
$ echo 'fs.inotify.max_user_watches=524288' | sudo tee /etc/sysctl.d/90-inotify.conf $ sudo sysctl --system
Another recursive-watch caveat
inotify sets watches per directory. A deep subdirectory created after monitoring starts can miss events during the brief window before its watch is added (a race). For trees where subdirectories appear frequently, design with this possible miss in mind.
Practical: detect changes and process automatically
Conclusion: Pipe
inotifywait -minto awhile readloop and process each file. Passing only the values you need via--formatkeeps parsing stable.
Process a CSV every time one is fully written into a watched directory:
#!/usr/bin/env bash
set -euo pipefail
WATCH_DIR=/var/spool/incoming
inotifywait -m -q \
--format '%w%f' \
-e close_write \
"$WATCH_DIR" |
while read -r filepath; do
case "$filepath" in
*.csv)
echo "Processing: $filepath"
# Run the real work here (ingest, transform, notify, etc.)
;;
*)
echo "Skipping: $filepath"
;;
esac
doneKey points:
-qsuppresses startup logs;--format '%w%f'passes only the full path- Narrowing to
close_writemakes "save complete" the only trigger read -rkeeps backslashes literal
Prevent concurrent runs
If you launch this monitor from cron or systemd, two instances could process the same file twice. Pair it with flock file locking to keep it to a single instance.
Watch out for editors' "atomic save"
Editors like vim may save by "writing a temp file, then renaming it over the original." In that case the change shows up as moved_to or create, not close_write. To reliably catch editor saves, watch the directory for both -e close_write -e moved_to.
inotifywait FAQ
Conclusion: Exit status is 0 on an event, 1 on error, and 2 on a
-ttimeout. Some network filesystems like NFS cannot detect changes made by other hosts.
Q. Exit if no event happens within a time limit
Use -t (--timeout) with a number of seconds. On timeout, the exit status is 2.
$ inotifywait -t 30 -e close_write /tmp/watchdir
Q. Exclude certain paths from watching
Pass an extended regular expression to --exclude (case-sensitive) or --excludei (case-insensitive).
$ inotifywait -m -r --exclude '\.git/' -e close_write /repo
Q. Can it watch files on NFS?
inotify only reports changes observed by the local kernel. On network filesystems like NFS, where another host writes the files, those changes may not be detected. Consider polling or another mechanism for shared storage.