# gow: a watch mode for the go command

> gow is invoked exactly like go but watches Go files and reruns the command when they change, with hotkeys for restart, extension filters, a terminal-clearing option and no configuration file. It is aimed at Go developers who want a `go run` loop during development without setting up a build tool.

**mitranim/gow** — Missing watch mode for Go commands. Watch Go files and execute a command like "go run" or "go test"

- Repository: https://github.com/mitranim/gow
- Stars: 908 · Forks: 29
- Language: Go
- License: Unlicense
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/mitranim-gow

## The go command, with a watcher attached

gow stands for Go Watch, and the README describes it as the missing watch mode for the `go` command. It is invoked exactly like `go`, but also watches Go files and reruns on changes.

That framing is the whole design. There is no separate project file, no daemon to configure and no plugin to install. You replace `go` with `gow` in the command you already type, and everything after it keeps working, because the first argument to `gow`, after the flags, can be any Go subcommand: `build`, `install`, `tool`, as the README puts it, you name it.

So the tool serves the everyday development loop rather than a build pipeline. `gow run .` starts the program and restarts it when a source file changes, and `gow vet` vets and re-vets on change. The reference to general purpose watchers and other runners in the Why section makes the comparison explicit: this is not a task runner, it is a `go` command with a filesystem watcher bolted on.

Portability is broad. The README says it runs on MacOS, Linux, BSD, WSL, and now also Windows, thanks to a contributor. The repository layout backs that up, with separate files for each platform: `gow_const_bsd.go` and `gow_const_linux.go`, and a family of process handling files for BSD, Darwin, Linux, the generic `ps` case and Windows, plus `gow_signal_unix.go` and `gow_signal_windows.go` for signal delivery.

The dependency list is short. `github.com/rjeczalik/notify` for filesystem events, `golang.org/x/term` for terminal handling, `golang.org/x/sys` for system calls, and `github.com/mitranim/gg` for the command line flags.

## The reasons it is not just a general purpose watcher

The Why section lists the arguments, and they are the ones that matter when you are choosing between tools.

It is Go specific and easy to remember, which is the argument you make when someone suggests a task runner. It has hotkeys, such as ctrl+r to restart, opt-in via `-r`. It ignores non-Go files by default, which is the difference between restarting when your source changes and restarting when your editor writes a swap file. The watcher is recursive, has no delays and does no polling, using the notify library, which is the standard approach for filesystem events on the platforms it supports. It is silent by default, with logging opt-in via `-v`. It leaves no garbage files behind. It properly clears the terminal on restart, opt-in via `-c`. It does not leak subprocesses. And it keeps its dependencies minimal.

Two of those are worth expanding because they are the ones you would notice if they were broken.

Not leaking subprocesses matters more than it sounds. A watch loop that restarts a server has to kill the previous process, and if it does not do that correctly you accumulate orphaned listeners on a port and a directory of stale processes. The alternative, killing the child process group, is why the repository has separate signal handling code for Unix and for Windows.

Clearing the terminal on restart is the other one. A restart that leaves the previous run's output above the new one turns a terminal into a scrollback log, and in a watch loop that grows without bound. The `-c` flag opts into clearing, and the default is to leave the output alone.

The counterweight to all of this is the flag-based configuration. There are no config files at all, which the README states plainly and calls suitable for small, simple projects, with larger projects expected to use a build tool such as Make instead, which is also sufficient for managing gow's configuration.

## Installing it and putting it on your PATH

Installation is the standard Go install, and it assumes you already have Go:

```sh
go install github.com/mitranim/gow@latest
```

That downloads the source and compiles the executable into `$GOPATH/bin/gow`, and the README says to make sure `$GOPATH/bin` is in your `$PATH` so the shell can discover the `gow` command. The example given is:

```sh
export GOPATH="$HOME/go"
export PATH="$GOPATH/bin:$PATH"
```

Alternatively you can run the executable by its full path, and at the time of writing `~/go` is the default `$GOPATH` for Go installations, though some systems may have a different one:

```sh
~/go/bin/gow
```

There is one platform gotcha worth carrying forward. On MacOS, if installation fails with dylib related errors, you may need to run `xcode-select --install` or install Xcode, and the README attributes this to gow's dependencies, which depend on C.

That last sentence is the useful part. gow itself is pure Go, but the notify library it uses relies on platform mechanisms that need C libraries, and the MacOS linker will complain about missing dylibs if the developer tools are absent. It is a one time environment fix rather than a problem with the tool.

The module itself targets Go 1.23.0 with a toolchain line of go1.24.0, so a Go install older than 1.23 will not build it.

## The flags that change the behaviour

The Usage section lists the commands, and the flags are where the practical decisions live.

```sh
# Start and restart on change
gow run .

# Pass args to the program
gow run . arg0 arg1 ...

# Run subdirectory
gow run ./subdir

# Vet and re-vet on change; verbose mode is recommended
gow -v vet

# Clear terminal on restart
gow -c run .

# Specify file extension to watch
gow -e=go,mod,html run .

# Enable hotkey support
gow -v -r vet

# Help
gow -h
```

The `-e` flag is the one you will reach for first. It sets the file extensions to watch, and by default gow ignores non-Go files, so a `.go` file change restarts the program while an edit to a `.json` or `.txt` file does not. That default is also what makes the tool usable with editors that touch many files on save.

`-g` selects the executable gow invokes, which defaults to `go`. It exists for scripting, and the README gives the case: if you want `go generate` to run before any other go operation, you create a local script and point gow at it. The script is two lines:

```sh
#!/bin/sh

go generate &&
go $@
```

and it is invoked with `gow -g=./go.sh -v -c run .`. The alternative the README offers is to write recipes in a makefile instead, and the repository ships an example makefile for exactly this purpose.

## Hotkeys need a real terminal, and raw mode has a race

The hotkey feature is the most interesting part of the tool and the most constrained.

The `-r` flag switches the terminal into raw mode, reads from stdin, interprets a small set of ASCII control codes and forwards everything else to the subprocess as-is. The supported codes are listed in the README, and the ones you will use are ctrl+C to kill the subprocess with SIGINT, ctrl+R to kill it with SIGTERM and restart, ctrl+T to kill it with SIGTERM, and ctrl+backslash for SIGQUIT. Two codes print information, ctrl+minus or ctrl+question mark printing the currently running command and ctrl+H printing help. The README notes that exact keys vary between terminal applications, for example ctrl+minus in MacOS Terminal versus ctrl+question mark in iTerm2.

The constraints are documented in detail and they are strict. The mode is only available in a TTY. There should be only one `gow -r` per terminal tab. No other process in that tab may use raw mode, with editors such as nano, vim and emacs named explicitly. And the subprocess does not need to read from stdin, because in raw mode the subprocess's stdin is always empty, immediately closed with EOF, and is not a TTY.

The race condition is the part worth internalising. gow processes do not coordinate with each other, so if several are trying to modify the terminal state, from cooked mode to raw mode and back, due to a race condition they may end up restoring the wrong state and leave the terminal in raw mode at the end. The README's advice is that most of them should be `gow -r=false`.

In Docker or any other non interactive environment, `-r` may produce errors such as `unable to read terminal state`, `inappropriate ioctl for device` and `operation not supported by device`. The example makefile in the repository has a `GOW_HOTKEYS` variable for detecting concurrent execution and turning hotkeys off.

## Watching templates without an infinite restart loop

Many Go programs, servers in particular, include template files and want to recompile them when they change. The README names two ways to do this with gow and rejects a third.

The easy but slow way is the extension flag:

```sh
gow -e=go,mod,html run .
```

That restarts the entire application on a change to any `.html` file in the current directory or its subdirectories. The warning is the important part. If the app also generates files with the same extensions, this could cause an infinite restart loop, and the fix is to ignore output directories with `-i`:

```sh
gow -e=go,mod,html -i=target run .
```

The loop is a genuine failure mode rather than a theoretical one. If the app writes a generated file into the watched tree and the watcher treats that write as a change, the process restarts, writes the file again, and the cycle never settles. The `-i` flag is what breaks it, and it is worth setting it before you hit the loop rather than after.

The smarter approach would be to watch the template files from inside the app and recompile them without restarting the whole program, and the README says that is out of scope for gow.

As an alternative, it points at a pure Go rendering system, github.com/mitranim/gax, by the same author, where templates are compiled in rather than watched from outside.

The README also has an Alternatives section, truncated in the copy used here, which covers the other tools the author considers comparable.

## The Unlicense, four dependencies and a quiet repository

The licence field on the repository says Unlicense, and the `unlicense` file sits at the top level next to the Go sources. The Unlicense is a public domain dedication, which is the most permissive position a project can take: the author waives copyright in the code and places it in the public domain, so you can use, modify and redistribute it with no conditions attached, and no attribution requirement.

The dependency count supports the minimalism claim. The go.mod requires `github.com/mitranim/gg` for flag parsing, `github.com/rjeczalik/notify` for filesystem events, `golang.org/x/sys` and `golang.org/x/term`. Four modules, two of them from the Go project itself and one by the same author.

The repository layout is a flat set of files in the root, which is unusual for a Go project and tells you this is a single binary rather than a library. There is `gow_main.go`, `gow_cmd.go`, `gow_flag.go`, `gow_opt.go`, `gow_misc.go`, `gow_sig.go`, `gow_stdio.go`, `gow_term.go`, `gow_watch_notify.go`, and `gow_test.go`, with the platform specific files beside them. There is a `makefile` with the concurrency detection logic, a `staticcheck.conf`, a `testdata/` directory and a `readme.md`.

On maintenance, the facts are modest. The repository has no GitHub releases, so the version lives in the module and the tags, and the last push was on 2026-05-15. The README's own tone, listing reasons why not to use other runners and pointing out that Windows support came from a contributor, suggests a small project maintained by one person.

That is not a criticism of a 900 line tool that does one thing. It is the reason the documentation is as detailed as it is, because the README is the only place the raw mode rules, the `-i` gotcha and the makefile pattern are written down.

## Conclusion

Adopt gow if you develop Go services locally and want a single command that reruns your program on save, especially if you want the restart hotkey and the freedom to ignore generated directories. Skip it if your project already has a working Make based workflow, since the README says larger projects typically use a build tool such as Make and that it is sufficient for managing gow's configuration, and skip it in containers, where the hotkey mode cannot work. Verify first that your templates are watched correctly and that your output directories are excluded with `-i`, since a self-triggering restart loop is the failure the README warns about. The licence field on the repository says Unlicense, the last push was on 2026-05-15, and the repository has no GitHub releases.

## FAQ

### What is gow and how is it different from a build tool?

gow is Go Watch, a missing watch mode for the `go` command. It is invoked exactly like `go` but also watches Go files and reruns on changes, and it has no config files: all configuration is done through CLI flags, with larger projects expected to use a build tool such as Make instead.

### How do I install gow?

With Go installed, run `go install github.com/mitranim/gow@latest`, which compiles the executable into `$GOPATH/bin/gow`. Make sure `$GOPATH/bin` is on your `$PATH`, or run it by full path such as `~/go/bin/gow`. The module targets Go 1.23.0 with a go1.24.0 toolchain.

### How do I use the gow restart hotkey?

Enable it with the `-r` flag, for example `gow -v -r vet`, then press ctrl+R to kill the subprocess with SIGTERM and restart it. This mode is only available in a TTY, should have only one `gow -r` per terminal tab, and the subprocess cannot read from stdin because it is always empty and closed.

### How do I stop gow from restarting in a loop when watching templates?

Ignore output directories with `-i`, for example `gow -e=go,mod,html -i=target run .`. The README warns that if the app generates files with the watched extensions it could cause an infinite restart loop.

### Can gow run a custom command instead of go?

Yes, with the `-g` flag. The README shows a local `go.sh` script that runs `go generate && go $@`, invoked as `gow -g=./go.sh -v -c run .`, and also suggests writing recipes in a makefile instead.

### What licence is gow released under?

The licence field on the repository says Unlicense, with an `unlicense` file at the top level, which places the code in the public domain without conditions. The repository has no GitHub releases, and the last push was on 2026-05-15.

## Sources

- [Issues](https://github.com/mitranim/gow/issues)
- [License: Unlicense](https://github.com/mitranim/gow/blob/master/LICENSE)
- [mitranim/gow on GitHub](https://github.com/mitranim/gow)
- [README](https://github.com/mitranim/gow/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mitranim-gow
