hotreload watches a project folder, rebuilds on change, and restarts the server.
- Watches your source tree recursively
- Debounces bursts of file changes
- Runs your build command (optional)
- Stops the old process and starts the new one
- Keeps logs readable with simple terminal UI output
- Process Group Isolation (
-pgid):hotreloadtargets the entire Unix process group to ensure no orphaned zombie processes are left behind on restart. - Mid-Build Context Cancellation: If a file changes while compiling, the in-flight build is instantly cancelled via
context.Contextto save CPU cycles and start the fresh build immediately. - Crash-Loop Backoff: If a syntax error causes the server to crash in under 3 seconds, a backoff penalty is applied to prevent a 100% CPU infinite restart loop.
- FD Watch Limit Fallback: Automatically maximizes the macOS soft FD limit at startup. For massive monorepos where OS limits are strictly capped, it gracefully degrades to a periodic polling engine for remaining directories.
- Smart Debouncing: Rapid, simultaneous file saves (e.g., "Save All") are coalesced into a single rebuild trigger using a thread-safe timer.
go install github.com/adarsh-sng/gomon/cmd/hotreload@latestOR (for now the repo is not public)
git clone https://github.com/adarsh-sng/gomon.git
cd gomon
go install ./cmd/hotreloadThis installs hotreload into $GOPATH/bin (usually ~/go/bin). Make sure it is in your PATH:
export PATH="$PATH:$HOME/go/bin"git clone https://github.com/adarsh-sng/gomon.git
cd gomon
make buildhotreload requires two core arguments:
- the root directory to watch (
--root) - the execution command (
--exec)
hotreload --root ./myproject \
--build "go build -o ./bin/server ./cmd/server" \
--exec "./bin/server"General form:
hotreload --root <path> --exec "<run command>" [--build "<build command>"] [flags]Example:
hotreload --root ./myproject \
--build "go build -o ./bin/server ./cmd/server" \
--exec "./bin/server"You can define defaults in .hotreload.yaml:
root: ./testserver
build: go build -o ./bin/server ./testserver
exec: ./bin/server
delay: 500ms
verbose: falseCLI flags override config values when both are set.
--root: directory to watch recursively (required)--build: build command to run on change (optional)--exec: run command for the built app (required)--delay: debounce delay before rebuild, default500ms--verbose: enable debug logging
- Use quotes around
--buildand--execvalues. - Paths are resolved from the current working directory where you run
hotreload. - If your executable path is relative, keep it consistent with your build output location.
.hotreload.yamlis loaded from--root(if set) or the current working directory.
make demomake demo-pythonmake demo-nodeGo project:
hotreload --root . \
--build "go build -o ./bin/server ./cmd/server" \
--exec "./bin/server"Python project:
hotreload --root ./testserver-python \
--exec "python3 ./testserver-python/app.py"Node project:
hotreload --root ./testserver-node \
--exec "node ./testserver-node/index.js"flowchart TD
A[main.go] --> B[MaximizeFDLimit]
A --> C[monitor.New]
A --> D[debounce.New]
A --> E[executor.New]
C --> C1[fsnotify loop]
C --> C2[poll loop]
C1 --> F[events channel]
C2 --> F
F --> G[main event loop]
G --> H[debounce trigger]
H --> I[executor.Cycle]
I --> J[kill process group]
I --> K[run build]
I --> L[start server]
L --> M[server process]
L --> N[cmd.Wait worker]
N --> M
B --> O[raise FD limit]
cmd/hotreload/main.go: CLI entrypoint, event loop, signal handlinginternal/monitor: recursive watcher usingfsnotify, with polling fallback when watch limits are hitinternal/debounce: coalesces rapid file changes before triggering rebuildinternal/executor: build + restart orchestration, crash loop backoff, process-group killinternal/ui: colored output + spinnerinternal/sysutil: OS helpers (FD limit bump on Unix)