Watchman Cheat Sheet
Overview
Watchman is a file watching service created by Meta (Facebook) that monitors files and directories for changes, then triggers actions based on configurable rules. It uses OS-level file notification APIs (inotify on Linux, FSEvents on macOS, ReadDirectoryChangesW on Windows) for efficient, low-overhead watching.
Watchman is commonly used by tools like React Native, Buck, Jest, and other build systems that need to efficiently detect file changes across large codebases. It maintains a persistent daemon process that indexes file trees and responds to queries about file state, making subsequent watches nearly instant.
Installation
# macOS
brew install watchman
# Ubuntu/Debian
sudo apt install watchman
# From source
git clone https://github.com/facebook/watchman.git
cd watchman
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build
sudo cmake --install build
# Verify
watchman version
watchman watch-list
Core Commands
| Command | Description |
|---|---|
watchman watch <dir> | Start watching a directory |
watchman watch-del <dir> | Stop watching a directory |
watchman watch-list | List all watched directories |
watchman trigger <dir> <name> <pattern> -- <cmd> | Set up a trigger |
watchman trigger-del <dir> <name> | Remove a trigger |
watchman trigger-list <dir> | List triggers for a directory |
watchman find <dir> <pattern> | Find files matching pattern |
watchman query <dir> <expr> | Query files with expression |
watchman since <dir> <clock> | Files changed since clock value |
watchman clock <dir> | Get current clock value |
watchman shutdown-server | Stop the watchman daemon |
watchman get-config <dir> | Show root configuration |
Watching Directories
# Watch a directory
watchman watch /path/to/project
# Watch current directory
watchman watch .
# Check watched roots
watchman watch-list
# Stop watching
watchman watch-del /path/to/project
# Watch a project (uses .watchmanconfig if present)
watchman watch-project /path/to/project
Triggers
Basic Triggers
# Run command when .js files change
watchman -- trigger /path/to/project build '*.js' -- npm run build
# Run linter on changed Python files
watchman -- trigger /path/to/project lint '*.py' -- python -m flake8
# Run tests when source or test files change
watchman -- trigger /path/to/project test '*.ts' '*.tsx' -- npm test
# List triggers
watchman trigger-list /path/to/project
# Remove a trigger
watchman trigger-del /path/to/project build
Advanced Trigger Configuration
# JSON trigger definition
watchman -j <<'EOF'
["trigger", "/path/to/project", {
"name": "build-on-change",
"expression": ["anyof",
["match", "*.ts"],
["match", "*.tsx"]
],
"command": ["npm", "run", "build"],
"append_files": false,
"stdin": ["name", "exists", "new", "size", "mode"]
}]
EOF
# Trigger with directory filter
watchman -j <<'EOF'
["trigger", "/path/to/project", {
"name": "test-src",
"expression": ["allof",
["match", "*.py"],
["dirname", "src"]
],
"command": ["pytest", "-x"]
}]
EOF
Queries
File Queries
# Find all JavaScript files
watchman -j <<'EOF'
["query", "/path/to/project", {
"expression": ["match", "*.js"],
"fields": ["name", "size", "mtime_ms"]
}]
EOF
# Find recently modified files
watchman -j <<'EOF'
["query", "/path/to/project", {
"since": "c:1234:5678",
"expression": ["anyof",
["match", "*.ts"],
["match", "*.tsx"]
],
"fields": ["name", "new", "exists"]
}]
EOF
# Complex query
watchman -j <<'EOF'
["query", "/path/to/project", {
"expression": ["allof",
["type", "f"],
["not", ["dirname", "node_modules"]],
["not", ["dirname", ".git"]],
["anyof",
["match", "*.ts"],
["match", "*.js"]
],
["since", "c:1234:5678"]
],
"fields": ["name", "size", "mtime_ms", "exists"]
}]
EOF
Configuration
.watchmanconfig
{
"settle": 20,
"idle_reap_age_seconds": 86400,
"ignore_dirs": [
"node_modules",
".git",
"dist",
"build",
"__pycache__",
".tox"
],
"fsevents_latency": 0.05
}
Global Configuration
# Configuration file locations:
# /etc/watchman.json (global)
# /usr/local/etc/watchman.json (Homebrew)
# $HOME/.watchman.json (user)
{
"root_restrict_files": [".git", ".watchmanconfig"],
"enforce_root_files": true,
"prefer_split_fsevents_watcher": true
}
Log and State Files
# Check log location
watchman get-sockname
# Common log locations
# macOS: /usr/local/var/run/watchman/<user>-state/
# Linux: /tmp/watchman-<user>-state/
# View logs
cat /usr/local/var/run/watchman/$USER-state/log
Advanced Usage
Subscriptions (Persistent Watches)
# Subscribe to file changes (used by IDEs and build tools)
watchman -j --persistent <<'EOF'
["subscribe", "/path/to/project", "my-subscription", {
"expression": ["anyof",
["match", "*.ts"],
["match", "*.tsx"]
],
"fields": ["name", "size", "exists", "type", "mtime_ms"]
}]
EOF
# Unsubscribe
watchman -j <<'EOF'
["unsubscribe", "/path/to/project", "my-subscription"]
EOF
State Management
# Get clock value (for incremental queries)
watchman clock /path/to/project
# Flush pending changes
watchman -j <<'EOF'
["flush-subscriptions", "/path/to/project", {
"sync_timeout": 10000
}]
EOF
# Debug: check watch status
watchman -j <<'EOF'
["debug-contenthash", "/path/to/project", {
"expression": ["name", "package.json"]
}]
EOF
Integration with Build Tools
# Used by Jest for fast file change detection
# jest.config.js
# { watchman: true }
# React Native uses watchman automatically
# Ensure it's installed before running:
npx react-native start
# Buck/Buck2 build system integration
# Watchman is auto-detected
Troubleshooting
| Issue | Solution |
|---|---|
| inotify limit reached (Linux) | Increase: echo 65536 | sudo tee /proc/sys/fs/inotify/max_user_watches |
| Daemon not starting | Check socket permissions; run watchman shutdown-server and retry |
| Stale watches | Run watchman watch-del-all then re-add watches |
| High CPU usage | Add node_modules and build dirs to ignore_dirs |
| macOS permission denied | Grant Full Disk Access to Terminal in System Preferences |
| Changes not detected | Check .watchmanconfig for overly broad ignore patterns |
# Restart the daemon
watchman shutdown-server
watchman version # auto-starts daemon
# Clear all watches
watchman watch-del-all
# Check daemon state
watchman get-sockname
watchman version
# Debug logging
watchman --logfile=/tmp/watchman-debug.log log-level 2
# Check inotify limits (Linux)
cat /proc/sys/fs/inotify/max_user_watches
# Permanently increase inotify limit
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p