Requirements: Linux with inotify-tools installed (sudo apt install inotify-tools / apk add inotify-tools). Verify with inotifywait --help. This package is a thin wrapper around the inotifywait binary, so if the binary isn't on the PATH of the PHP process, nothing works. macOS and Windows are not supported (no FSEvents / ReadDirectoryChangesW backend).
Install:
composer require octopyid/laravel-inotify
php artisan vendor:publish --provider="OctopyID\Inotify\InotifyServiceProvider"
Because the package is archived (last release 2022-06-28), pin it in composer.json and commit composer.lock immediately. Do not run composer update on a Laravel 10/11 app without checking composer.json constraints in vendor/octopyid/laravel-inotify — the Illuminate constraints are almost certainly capped at ^8.0|^9.0.
Where to look first, in order:
vendor/octopyid/laravel-inotify/src/ — the whole package is a handful of files. Read the command class and the watcher class before trusting the README.config/inotify.php (after publishing) — the paths/events/ignore shape.php artisan list | grep inotify — finds the shipped console command (typically inotify:watch).README.md on GitHub — it's the only documentation, and method names in it are authoritative over anything else.First use case (five minutes): point the watcher at storage/logs and echo new lines, or at app/ and print the event name + path. That confirms three things at once: the binary is reachable, the config is loaded, and the event format parses. Get that green before wiring handlers.
# sanity check outside Laravel first — this is what the package shells out to
inotifywait -m -r -e create,modify,delete,move --format '%e %w%f' app/
If that prints events and the artisan command doesn't, the bug is in your config or the PHP process environment, not in inotify.
1. Config-driven watchers for repeatable, committed behavior. Keep the paths and event lists in config/inotify.php so the watcher is reproducible on every developer machine. Reserve inline facade usage for one-off scripts in tinker.
2. Treat the watcher as a dispatcher, not a worker. The watcher is a single long-running PHP process. Doing real work inside the callback (running a full test suite, an HTTP call, an image resize) blocks every subsequent event. The pattern that scales:
// callback should be cheap and idempotent
Inotify::watch(base_path('storage/app/inbox'))
->onCreate(fn (string $path) => ImportFileJob::dispatch($path)->onQueue('imports'));
3. Cache-busting recipes. These are the day-to-day wins and each is two lines:
Artisan::call('view:clear') (avoids the "my edit didn't show up" 15-minute rabbit hole).config/*.php changed → Artisan::call('config:clear').routes/*.php changed → Artisan::call('route:clear')..env changed → Artisan::call('config:clear') plus a note to restart Octane/queue workers, because env changes don't reach already-booted processes.4. Poor-man's dev loop. Watch app/, tests/, routes/ and shell out to vendor/bin/phpunit --filter or vendor/bin/pint --dirty. Debounce by tracking the last-fired timestamp per path and skipping anything inside 300–500 ms. This is the single most useful thing the package does, and it's also the thing spatie/phpunit-watcher and Vite's own watcher already do better.
5. Event-driven integration for inbound files. A drop folder (storage/app/inbox, an SFTP landing zone, a mounted S3 fuse path) with a create handler that dispatches a queued job is a legitimate production pattern — but run it under Supervisor/systemd with autorestart, not in a terminal.
6. Process supervision:
[program:inotify]
command=php /var/www/app/artisan inotify:watch
autorestart=true
numprocs=1
redirect_stderr=true
stdout_logfile=/var/log/app/inotify.log
7. Testing handlers. Don't test the watcher by waiting for real filesystem events. Extract the callback body into an invokable class or a job, and unit-test that directly with a fake path. The inotify layer itself has one behavior worth asserting: that a given event name maps to the right handler.
composer why-not octopyid/laravel-inotify against your target framework version. If it fails, fork it into app/Support/Inotify — it's small enough that vendoring is cheaper than fighting constraints.ENOSPC / "no space left on device" is the classic inotify failure and it means watch descriptors, not disk. Raise fs.inotify.max_user_watches (and max_user_instances) via sysctl; also add vendor, node_modules, storage/framework, .git to the ignore list — a recursive watch on node_modules alone can eat tens of thousands of watches.CREATE + MODIFY (×N, one per write chunk) + MOVED_TO. Deduplicate by path with a short cooldown before dispatching. Do not assume one event per save.inotifywait needs -L/explicit targets. If you watch a symlinked deploy path (current -> releases/xyz), you may be watching nothing after a deploy.base_path(...)) before comparing or storing; the %w%f output is whatever you passed to inotifywait.try/catch and Log::error — an uncaught exception may kill the watcher loop silently.queue:work). Restart the process nightly or after N events.Symfony\Component\Process\Process around inotifywait and keeping the same config shape. That also removes the abandoned dependency without changing your handlers.laravel/pail or tail -f; test watching → spatie/phpunit-watcher or phpunit --watch; CI file triggers → your CI provider's path filters. Inotify is the right tool only for "run arbitrary PHP when a file on this Linux box changes," and even then, verify you actually need the wrapper.How can I help you explore Laravel packages today?