Control panels

Using inotify-tools to Monitor File System Changes in Real-Time on Linux

Install inotify-tools on a Linux VPS, watch directories with inotifywait, tune the kernel watch limits and run a persistent file-change watcher as a systemd service.

AdvancedLinux server6 min read

You can monitor file system changes in real time on Linux with inotify-tools, a small package that exposes the kernel's inotify API through two commands: inotifywait and inotifywatch. This guide installs the tools, watches a directory tree, raises the kernel limits that large projects need, and runs a permanent watcher under systemd so you get a log line every time a file is created, written, moved or deleted.

It is a practical way to trigger deploy hooks, catch unexpected writes to /etc, or audit what an application is doing with its own storage.

Before you start

  • A Linux VPS running Ubuntu 22.04/24.04, Debian 12, AlmaLinux 8–9 or Rocky Linux 8–9.
  • A sudo-capable user. If you only have root, see create a sudo user on Linux first.
  • SSH access to the server — connect to your Linux VPS with SSH if you have not set up keys yet.
  • A directory worth watching. Examples below use /var/www/example.com and /etc.

Inotify is local-only. It reports changes made on the machine where it runs, not edits made to an NFS or CIFS share from another host.

Install inotify-tools

Install from the distribution repository:

Bash
# Ubuntu and Debian
sudo apt update && sudo apt install -y inotify-tools

# AlmaLinux and Rocky Linux
sudo dnf install -y inotify-tools

Check that both binaries are present:

Bash
inotifywait --help | head -n 5
inotifywatch --help | head -n 5

inotifywait waits for events; inotifywatch counts them over a period. This guide focuses on inotifywait.

Watch a directory in real time

The -m flag keeps the process running, -r adds subdirectories, and -e lists the events you care about:

Bash
sudo inotifywait -m -r -e create,close_write,delete,move \
  --timefmt '%F %T' --format '%T %e %w%f' \
  /var/www/example.com

Open a second terminal as the deploy user and touch a file:

Bash
touch /var/www/example.com/test.txt

The first terminal prints something like:

2026-01-01 10:12:04 CREATE /var/www/example.com/test.txt
2026-01-01 10:12:04 CLOSE_WRITE,CLOSE /var/www/example.com/test.txt

Useful events include create, delete, modify, close_write, moved_to, moved_from, attrib and delete_self. Prefer close_write over modify for deploy hooks: it fires once when a writer closes the file, while modify fires on every write.

Filter noise with a regular expression:

Bash
sudo inotifywait -m -r -e close_write,moved_to \
  --exclude '\.(log|tmp|swp)$' \
  --format '%w%f' /var/www/example.com

Add -t 30 to exit after 30 seconds with no events — handy inside scripts and cron jobs.

Raise the inotify watch limits

Each watched directory consumes one watch, and the kernel caps how many a user may hold. Check the current values:

Bash
cat /proc/sys/fs/inotify/max_user_watches
cat /proc/sys/fs/inotify/max_user_instances
cat /proc/sys/fs/inotify/max_queued_events

Warning: Raising these limits increases kernel memory use, roughly proportional to the watch count. Read the current numbers first and never set a value lower than what is already there.

Create /etc/sysctl.d/99-inotify.conf:

INI
fs.inotify.max_user_watches = 262144
fs.inotify.max_user_instances = 512
fs.inotify.max_queued_events = 32768

Apply and confirm:

Bash
sudo sysctl --system
cat /proc/sys/fs/inotify/max_user_watches

Watching a large tree such as a full container runtime or a big Git checkout is the usual reason to increase these. Only recursive watches over many thousands of directories need it.

Run the watcher as a systemd service

A background process started from an SSH session dies when the session ends. Put the watcher in a script and a service instead.

Bash
sudo tee /usr/local/bin/watch-www.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
WATCH_DIR="/var/www/example.com"
exec /usr/bin/inotifywait -m -r -e create,close_write,delete,move \
  --timefmt '%F %T' --format '%T %e %w%f' "$WATCH_DIR"
EOF
sudo chmod 755 /usr/local/bin/watch-www.sh

Create /etc/systemd/system/watch-www.service:

INI
[Unit]
Description=Watch /var/www/example.com for file changes
After=network.target

[Service]
Type=simple
User=deploy
Group=deploy
ExecStart=/usr/local/bin/watch-www.sh
Restart=always
RestartSec=5
StandardOutput=append:/var/log/watch-www.log
StandardError=append:/var/log/watch-www.log

[Install]
WantedBy=multi-user.target

The deploy user needs read and execute access on every directory in the tree; see chmod and chown if the log stays empty. Enable and start it:

Bash
sudo touch /var/log/watch-www.log
sudo chown deploy:deploy /var/log/watch-www.log
sudo systemctl daemon-reload
sudo systemctl enable --now watch-www.service
sudo systemctl status watch-www.service

Use it for deploys and auditing

A common pattern is a loop that reacts to a single event, such as rebuilding a cache after an upload lands:

Bash
#!/usr/bin/env bash
set -euo pipefail
WATCH_DIR="/var/www/example.com/uploads"
while inotifywait -r -e close_write,moved_to --format '%w%f' "$WATCH_DIR"; do
  echo "$(date -Is) changed: ${REPLY:-$WATCH_DIR}"
  # run your rebuild or notification command here
done

For auditing, watch configuration directories and separate sessions from bulk edits. To catch changes to system accounts, watch /etc/passwd, /etc/shadow and /etc/sudoers individually rather than all of /etc, which is noisy on a busy server. Also watch /etc/ssh/sshd_config after hardening, and pair this with the resource and log monitoring described in monitor CPU, memory, disk and logs on a Linux server.

Tip: inotify queues events in the kernel. If your handler is slow or the queue overflows, events are dropped silently. Use a short, non-blocking handler and raise fs.inotify.max_queued_events if you see gaps.

Check it works

  1. Confirm the service is active: systemctl is-active watch-www.service should print active.
  2. Generate an event: sudo -u deploy touch /var/www/example.com/verify.txt.
  3. Read the log tail: sudo tail -n 5 /var/log/watch-www.log. You should see the CREATE line for verify.txt.
  4. Delete the file and confirm a DELETE line appears.
  5. Watch the live counter while a test runs:
Bash
sudo inotifywatch -r -e create,delete -t 20 /var/www/example.com

Troubleshooting

  • "Failed to watch … upper limit on inotify watches reached" — the user has too many watches. Set fs.inotify.max_user_watches as shown above and restart the service.
  • No events at all on a network share — inotify does not report remote edits on NFS, CIFS or SMB mounts. Run the watcher on the server that owns the files.
  • Missing events during heavy writes — the queue is overflowing or you are relying on modify. Switch to close_write and increase fs.inotify.max_queued_events.
  • Duplicate lines for one file save — editors write to a temporary file and rename it, producing CREATE, MOVED_TO and CLOSE_WRITE. Filter down to moved_to,close_write or exclude *.swp and *.tmp.
  • Permission denied on a subdirectory — the service user cannot traverse the tree. Fix ownership and mode on the parent directories, then reload the unit.
  • Service restarts in a loop — the watched directory was deleted or renamed. Check journalctl -u watch-www.service and confirm the path still exists.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.