watchexec - Run Commands on File Changes Cheatsheet
watchexec watches a set of files or directories and runs a command whenever they change. It is language- and project-agnostic — no config file required — and handles the fiddly parts of file-watching for you: debouncing rapid changes, respecting .gitignore, restarting long-running processes cleanly, and passing the changed paths to your command. Use it to auto-run tests, restart a dev server, or rebuild on save.
Installation
| Platform | Command |
|---|
| Cargo (all platforms) | cargo install watchexec-cli |
| macOS (Homebrew) | brew install watchexec |
| Arch Linux | sudo pacman -S watchexec |
| Debian/Ubuntu | download the .deb from Releases |
| Windows (Scoop) | scoop install watchexec |
| Verify | watchexec --version |
Basic Usage
| Command | Description |
|---|
watchexec "cargo test" | Run tests on any change |
watchexec -e py "pytest" | Only watch .py files |
watchexec -e js,ts,jsx "npm run build" | Watch multiple extensions |
watchexec -- ls -la | Everything after -- is the command |
watchexec --help | Full option list |
Filtering What to Watch
| Option | Description |
|---|
-e, --exts py,rs | Only trigger on these file extensions |
-w, --watch DIR | Watch a specific path (repeatable) |
-i, --ignore GLOB | Ignore paths matching a glob |
--ignore-file FILE | Load ignore patterns from a file |
--no-vcs-ignore | Don’t use .gitignore rules |
-f, --filter GLOB | Only watch files matching a glob |
Process Management
| Option | Description |
|---|
-r, --restart | Restart the command on change (kill + rerun) |
-s, --signal SIG | Signal to send when restarting (e.g. SIGTERM) |
--stop-timeout 5s | Grace period before force-kill |
-o, --on-busy-update MODE | queue, do-nothing, restart, or signal |
-n | No process group (send signal to the direct child only) |
Use -r for long-running processes (servers), and the default for one-shot commands (tests, builds).
Debouncing & Timing
| Option | Description |
|---|
--debounce 200ms | Wait for changes to settle before running |
--delay-run 1s | Delay before the command runs |
-p, --postpone | Don’t run until the first change (skip initial run) |
Useful Behavior
| Option | Description |
|---|
-c, --clear | Clear the screen before each run |
--emit-events-to environment | Pass changed paths via env vars |
-N, --notify | Desktop notification on completion |
--project-origin DIR | Set the project root for ignore rules |
-v | Verbose (debug what triggers) |
Environment Variables (Changed Paths)
When events are emitted to the environment, your command can see what changed:
| Variable | Contains |
|---|
WATCHEXEC_COMMON_PATH | Common prefix of changed paths |
WATCHEXEC_WRITTEN_PATH | Files written |
WATCHEXEC_CREATED_PATH | Files created |
WATCHEXEC_REMOVED_PATH | Files removed |
Common Workflows
# Auto-run the test suite on any source change
watchexec -e rs "cargo test"
# Restart a dev server cleanly on change
watchexec -r -e go "go run ./cmd/server"
# Rebuild and clear the screen, ignoring the dist folder
watchexec -c -i "dist/**" -e ts "npm run build"
# Only after changes settle (avoid thrashing on bulk saves)
watchexec --debounce 500ms -e py "pytest -x"
| Tool | Model |
|---|
| watchexec | Standalone, config-free file watcher |
| entr | Minimal, reads file list from stdin |
| nodemon | Node-focused, restarts node apps |
| mise/just + watchexec | Task runners that shell out to watchexec |
Task runners like just and mise often pair with watchexec to add file-watching to their recipes.
Resources