incron: Cron for Filesystem Events, System-Wide and Persistent

2026-08-28

entr and fatrace get all the attention, but they're the wrong shape for a lot of real jobs. entr blocks a terminal and re-runs a whole command list on every touch. fatrace observes but does nothing. What you actually want is cron for filesystem events: a daemon, per-user tables, persistent across reboots, one line per rule, with the full inotify event mask and event details passed as substitutions. That's incron, written by Lukáš Jelínek around 2006 and still shipping in Debian, Ubuntu, and EPEL as incron.

The interface deliberately apes cron. There's incrontab -e per user, /etc/incron.d/* for packages, and an incrond daemon under systemd. Each line has three fields: path, event mask, command with substitutions.

# SFTP drop box: process each fully-written upload exactly once
/srv/incoming    IN_CLOSE_WRITE,IN_MOVED_TO    /usr/local/bin/ingest "$@/$#"

# Reload nginx after any config change, but only if the config parses
/etc/nginx/conf.d    IN_CLOSE_WRITE,IN_DELETE    /bin/sh -c 'nginx -t && nginx -s reload'

# Kick off a rebuild when the source tree changes, no self-loops
/home/build/src    IN_MODIFY,IN_NO_LOOP    /usr/local/bin/rebuild.sh

The substitutions are the point:

So one incrontab line handles any new file in the directory, with the exact filename and the exact event, without you writing a shell loop around inotifywait.

Why it beats the obvious alternatives. A systemd .path unit only understands PathChanged, PathModified, PathExists, PathExistsGlob, DirectoryNotEmpty, and it needs a paired .service unit. incron gives you the whole inotify vocabulary — IN_CLOSE_WRITE, IN_MOVED_TO, IN_ATTRIB, IN_DELETE_SELF — in a one-liner. A cron job polling find -newer at one-minute granularity misses bursts and hammers the disk; incron is event-driven and immediate.

The traps the docs bury. These are the ones that bite people:

Where it shines. SFTP intake boxes where you want per-file processing without a polling loop. Reloading a daemon whenever its config directory changes. Kicking off rsync or restic after a directory is quiet. Auto-importing dropped photos into a library. Triggering a lint run when a shared mount changes upstream. Anywhere you would have written a while inotifywait ...; do ... done as a systemd service — that's an incrontab line instead.

Key Takeaway: incron is what you actually wanted when you reached for entr, cron, or a systemd path unit: a persistent daemon with a cron-shaped table that fires shell commands on the full inotify event set, with the triggering filename handed to your command for free.

All newsletters