CLI tool
air-verse/air avatar
air-verse/air

air-verse/air: live reload for Go apps, and where it stops being the right tool

Live reload for Go apps. The legacy build.bin field is deprecated and will be removed in a future release, so prefer the entrypoint form going forward.

24,019 stars923 forksGoGPL-3.0

At a glance

What is it?
Air rebuilds and restarts a Go program when files change, driven by a .air.toml config. It is a development-time watcher, and the README says plainly that it has nothing to do with hot-deploy for production.
Who is it for?
Air suits Go developers who want their binary rebuilt and restarted on save without wiring up a watcher themselves, and it is a poor fit for anyone looking for production hot-deploy, since the README states the tool has nothing to do with that.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 34 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What air actually removes from the Go edit loop

Go compiles fast, but the loop around compilation is manual: stop the running server, rebuild, start it again, and repeat after every edit. Air takes that loop over. You run a single command in the project root, leave it running, and it watches your source tree, rebuilds the binary, and restarts the process when something changes. The README frames the audience directly: run air in your project root directory, leave it alone, and focus on your code.

The scope is deliberately narrow. Air is a development-time utility, and the README states that the tool has nothing to do with hot-deploy for production. That single sentence rules out a whole class of misuse. Air is not a process supervisor, not a zero-downtime deploy mechanism, and not something you ship in a container that serves traffic. It is the thing you keep in a terminal tab while you work.

The feature list is short and concrete: colorful log output, a customizable build command or any command, excluding subdirectories, watching new directories after Air has started, and configurable .env file loading. Each of those maps to a real annoyance in a Go project, particularly the last two. New directories appearing mid-session is common when you add a package, and .env loading matters because the built binary inherits the environment Air runs in.

How the watcher, build step and entrypoint fit together

The dependency list in go.mod tells you most of the mechanism. github.com/fsnotify/fsnotify is the filesystem notification layer, github.com/pelletier/go-toml parses the configuration, github.com/joho/godotenv handles the .env loading, and github.com/fatih/color produces the colored logs. There is also a dependency on github.com/gohugoio/hugo, which is unusual for a tool this size and worth noting if you care about binary size or the transitive dependency graph.

The data flow is: a file event arrives from fsnotify, Air filters it against the include and exclude rules from the config, and if the change is relevant it runs the build command. The build command is yours, not a fixed one, so Air does not assume a package layout. When the build succeeds, Air executes the entrypoint, which is the binary the build produced plus any runtime arguments. Configuration comes from .air.toml in the current directory when present, and from built-in defaults when it is not.

The entrypoint is the part that has been changing. The README notes that the legacy build.bin field is deprecated and will be removed in a future release, and that the entrypoint form should be preferred. build.entrypoint points at the binary generated by build.cmd and describes how it should be executed. The value can be a string, meaning just the executable, or an array of strings, where the first element is the executable and the rest are arguments. An executable without a path separator is resolved through $PATH; one with a separator is resolved relative to the configured root. If you are copying an older .air.toml from another project, that is the field to check first.

Installing air and getting a first rebuild

The README recommends go install, and states that it requires Go 1.25 or higher. Note the discrepancy worth knowing about: the module file declares go 1.26.0, so a 1.25 toolchain may refuse to build the module even though the installation instructions name 1.25 as the floor. Check your toolchain before you start.

bash
go install github.com/air-verse/air@latest

After that, make sure your Go bin directory is on PATH, otherwise the shell will not find the binary:

bash
export PATH="$PATH:$(go env GOPATH)/bin"

There is also a project-scoped install using the Go 1.25 tool directive, which pins Air into the module rather than the global bin directory:

bash
go get -tool github.com/air-verse/air@latest
go tool air -v

If you prefer not to build from source, the README lists Homebrew (brew install go-air), Scoop (scoop install air), mise (mise use -g air), an install.sh script, goblin.run, and a Docker image at cosmtrek/air. Homebrew's formula is named go-air rather than air, which is the kind of detail that wastes ten minutes if you guess.

Now the first real use. Enter a project and run Air with no arguments. It tries .air.toml in the current directory and falls back to defaults if there is none:

bash
cd /path/to/your_project
air

To get a file you can edit rather than an implicit default, generate one once:

bash
air init
air

Air then prints its startup banner, builds, and runs. Edit a .go file and you should see a rebuild followed by a restart. To silence the banner, set misc.startup_banner to an empty string in .air.toml; to replace it with your own text, give it a value.

Runtime arguments go to the built binary, not to Air. Anything after the air command is forwarded, and -- separates the two when a flag could be ambiguous:

bash
air server --port 8080
air -- -h
air -c .air.toml -- -h

You can also skip the config file entirely for a one-off, since Air accepts its config fields as command-line arguments:

bash
air --build.cmd "go build -o bin/api cmd/run.go" --build.entrypoint "./bin/api"

List-valued arguments accept a comma-separated string or repetition, and repeated values are appended in order. The README gives --env_files ".env,.env.local" --env_files ".env.secret" as equivalent to a single comma-separated list. That matters when a Makefile generates the command line.

Where air is the wrong tool, and the failure modes to expect

The clearest boundary is the one the README draws itself: this is not hot-deploy for production. If you need a process that survives crashes, rotates logs, or drains connections on restart, Air does none of that. It kills and restarts. Running it as PID 1 in a production container would give you a restart loop with no supervision semantics, and the official Dockerfile reflects the intended use, since it exists to run Air inside a development container.

The second boundary is build cost. Air reruns your build command on relevant file changes. In a small service that is imperceptible. In a large module with code generation, cgo, or vendored dependencies, every save pays the full build price, and Air has no incremental compilation of its own. The README's build customization is the escape hatch: you can point build.cmd at a narrower target, but you then own the consequences of a partial build.

The third is configuration drift. Because Air accepts its fields as CLI arguments and merges them over .air.toml, a project can accumulate behavior that is not visible in the config file. A Makefile that appends --build.exclude_dir silently changes what is watched. That is convenient and it is also how two developers end up with different reload behavior on the same repository.

Finally, the deprecation itself is a migration cost. Any .air.toml using build.bin will need to move to build.entrypoint before the field is removed. The README does not document a rollback path or a compatibility shim, so the migration is a config edit you make once and verify by hand.

Air against a plain go run loop or a Makefile with entr

The obvious alternative is a shell loop: a Makefile target that watches files and re-runs go run. It has no dependencies, no config file, and no learning curve, and for a single main package it is often enough. The difference is everything Air adds around the edges. A shell loop does not track newly created directories, does not exclude subdirectories by pattern, does not load .env files, and does not give you a startup banner distinguishing a rebuild from a restart. Air also accepts its configuration as command-line arguments, so the shell-loop style stays available inside it.

A second alternative is a general-purpose file watcher such as entr, combined with your own build and run commands. That approach is more composable: entr knows nothing about Go and will happily trigger any command. The trade-off runs the other way. You write the filtering rules yourself, and you own the restart logic, including the part where the previous process must actually die before the new one binds the port. Air handles that lifecycle as its core job rather than as a shell script you maintain.

The honest comparison is that Air is a Go-specific convenience layer over fsnotify plus a process lifecycle. If your project is one package and one binary, the convenience is small. If it has build tags, platform-specific overrides, environment files, and directories you never want to watch, the config file earns its place.

Maintenance, licensing and the upgrade cost you are signing up for

The repository is not archived, and the last push was on 2026-08-01, which is recent enough that the project is being worked on rather than frozen. Releases have been frequent: v1.67.4 on 2026-08-01, v1.67.3 on 2026-07-26, and v1.67.2 on 2026-07-22. The version numbering suggests incremental changes rather than long gaps, and the presence of a .goreleaser.yml, a Makefile with test and ci targets, and a smoke_test directory indicates a project that builds and tests itself rather than one maintained by hand.

Upgrade cost is low but not zero. Air is a single binary with no runtime dependencies, so upgrading means replacing the binary. The cost comes from configuration compatibility: the build.bin deprecation is the live example, and it tells you that .air.toml is treated as a public interface that can change. Because the module declares go 1.26.0, a pinned older toolchain in CI can block an upgrade even when the Air release itself is compatible. The Makefile's AIRVER fallback to dev when git describe finds no tags is also worth knowing: a build outside a git checkout reports a version that is not a release identifier.

The licence is GPL-3.0. That is a copyleft licence, and it applies to the Air binary and its source, not to the Go program you are developing with it. Running Air as a development tool does not change the licence of your own code. Distributing a modified Air, or embedding its source into a product, is a different question, and the LICENSE file and the project's own guidance are the places to read rather than an article. If your organization has rules about GPL tooling in the build environment, this is the point to raise with whoever owns that policy.

Editorial conclusion

Air suits Go developers who want their binary rebuilt and restarted on save without wiring up a watcher themselves, and it is a poor fit for anyone looking for production hot-deploy, since the README states the tool has nothing to do with that. Before adopting it, verify two things in your own checkout: that your Go toolchain satisfies the go.mod requirement of go 1.26.0, and that the entrypoint you configure actually matches the path your build.cmd produces, because the deprecated build.bin field is the older way of expressing the same thing and is scheduled for removal.

Frequently asked questions

How do I install air-verse/air?

The README recommends go install github.com/air-verse/air@latest, which it says requires Go 1.25 or higher, followed by adding $(go env GOPATH)/bin to PATH. Alternatives listed in the README include brew install go-air, scoop install air, mise use -g air, the install.sh script, goblin.run, and the cosmtrek/air Docker image.

What is the .air.toml file in air-verse/air?

It is Air's configuration file. Air first tries .air.toml in the current directory and falls back to built-in defaults if it is not found, and air init writes one with the default settings. The README points to air_example.toml for every available option.

Can I use air-verse/air in production?

No. The README states that the tool has nothing to do with hot-deploy for production, and it describes Air as a live-reloading utility for developing Go applications. The official Dockerfile exists to run Air inside a development container.

What replaced the build.bin field in air-verse/air?

The build.entrypoint field. The README says build.bin is deprecated and will be removed in a future release, and that entrypoint should be preferred. The entrypoint value can be a string naming only the executable, or an array of strings where the first element is the executable and the rest are arguments.

Official sources

  1. Official README
  2. Project repository
  3. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/air-verse-air.svg)](https://hysenlabs.com/projects/air-verse-air)