☁️ Live reload for Go apps


Air Go Codacy Badge Go Report Card codecov

☁️ Live reload for Go apps



When I get started with developing websites in Go and gin framework, it's a pity that gin lacks live-reloading function. In fact, I tried fresh and it seems not much flexible, so I intended to rewrite it in a better way. Finally, Air's born. In addition, great thanks to pilu, no fresh, no air :)

Air is yet another live-reloading command line utility for Go applications in development. Just air in your project root directory, leave it alone, and focus on your code.

NOTE: This tool has nothing to do with hot-deploy for production.


  • Colorful log output
  • Customize build or binary command
  • Support excluding subdirectories
  • Allow watching new directories after Air started
  • Better building process



The classic way to install

go get -u github.com/cosmtrek/air

macOS, Linux, Windows

# binary will be $(go env GOPATH)/bin/air
curl -sSfL https://raw.githubusercontent.com/cosmtrek/air/master/install.sh | sh -s -- -b $(go env GOPATH)/bin

# or install it into ./bin/
curl -sSfL https://raw.githubusercontent.com/cosmtrek/air/master/install.sh | sh -s

air -v

P.S. Great thanks mattn's PR for supporting Windows platform.

Docker way

Please pull this docker image cosmtrek/air.

docker run -it --rm \
    -w "<PROJECT>" \
    -e "air_wd=<PROJECT>" \
    -v $(pwd):<PROJECT> \
    -c <CONF>

For example, one of my project runs in docker:

docker run -it --rm \
    -w "/go/src/github.com/cosmtrek/hub" \
    -v $(pwd):/go/src/github.com/cosmtrek/hub \
    -p 9090:9090 \


For less typing, you could add alias air='~/.air' to your .bashrc or .zshrc.

First enter into your project

cd /path/to/your_project

The simplest usage is run

# firstly find `.air.toml` in current directory, if not found, use defaults
air -c .air.toml

While I prefer the second way

# 1. create a new file
touch .air.toml

# 2. paste `air_example.toml` into this file, and **modify it** to satisfy your needs.

# 3. run air with your config. If file name is `.air.toml`, just run `air`.

See the complete air_example.toml


air -d prints all logs.


Please note that it requires Go 1.13+ since I use go mod to manage dependencies.

# 1. fork this project

# 2. clone it
mkdir -p $GOPATH/src/github.com/cosmtrek
cd $GOPATH/src/github.com/cosmtrek
git clone [email protected]:<YOUR USERNAME>/air.git

# 3. install dependencies
cd air
make ci

# 4. explore it and happy hacking!
make install

BTW: Pull requests are welcome~


Buy Me A Coffee

Huge thanks to the following supporters. I've always been remembering your kindness.

  • Peter Aba
  • Apostolis Anastasiou
  • keita koga


GNU General Public License v3.0

  • Terminal text gets garbled on Windows

    Terminal text gets garbled on Windows

    Whenever running air on windows the text has issues printing.

    λ c:\Users\sonar\.air .
         /\     (_)
        /  \     _   _ __
       / /\ \   | | | '__|
      / ____ \  | | | |
     /_/    \_\ |_| |_|
    Live reload for Go apps :)
    [19:38:14.574] watching .
    [19:38:14.577] watching protos
    [19:38:14.579] !exclude tmp
    [19:38:14.580] building...
    [19:3[81:91:53.83:2155]. 3!2e6x]c l!uedxec ltumdpe
    19:38:15.329] !exclude tmp
    [19:38:15.341] running...

    I get this issue running in git bash as well as cmd.

    opened by SonarBeserk 16
  • Process not being killed on Windows

    Process not being killed on Windows

    Hello! First of all, thank you for this awesome package :)

    Due to Air's current implementation of starting processes on Windows (with start /b), the PID returned by cmd.Process.Pid is of a process that already exited (start's PID, not what it started), hence the debug error failed to kill PID ###, error: exit status 128 is returned.

    When trying to run the TASKKILL command manually, I noticed the following error: ERROR: The process "###" was not found., which confirms my suspicion.

    I'm thinking of a good way to fix this, since just TASKKILLing the process by name isn't a good idea.

    Is start /b even necessary? I'll try to modify the code and run some tests without it.

    If that doesn't work, maybe try to get the PID by executable path? Is that even possible? If so, just check it against c.Build.FullBin and use that on TASKKILL.

    I don't think it would be a good idea to try to parse TASKLIST as the language might change per system, etc. (also it doesn't even seem to return the executable path).. but I don't see other alternatives as listing processes in Go seems to be a nightmare..


    opened by HeCorr 16
  • Go 1.15: failed to build

    Go 1.15: failed to build


    We did try to update to Go 1.15 but we're having this issue starting air:

    failed to build, error: fork/exec /bin/sh: Setctty set but Ctty not valid in child

    just after building... step. We use Go 1.15 alpine docker image.

    Tested with Air v1.12.4


    opened by guyguy333 10
  • Hot reloading does not work when placing main.go inside cmd/ directory

    Hot reloading does not work when placing main.go inside cmd/ directory

    My project structure looks like this: image


    # Config file for [Air](https://github.com/cosmtrek/air) in TOML format
    # Working directory
    # . or absolute path, please note that the directories following must be under root.
    root = "."
    tmp_dir = "tmp"
    # Just plain old shell command. You could use `make` as well.
    cmd = "go build -o ./tmp/main ./cmd/main.go"
    # Binary file yields from `cmd`.
    bin = "tmp/main"
    # Customize binary.
    full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
    # Watch these filename extensions.
    include_ext = ["go", "tpl", "tmpl", "html"]
    # Ignore these filename extensions or directories.
    exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
    # Watch these directories if you specified.
    include_dir = []
    # Exclude files.
    exclude_file = []
    # This log file places in your tmp_dir.
    log = "air.log"
    # It's not necessary to trigger build each time file changes if it's too frequent.
    delay = 1000 # ms
    # Stop running old binary when build errors occur.
    stop_on_error = true
    # Send Interrupt signal before killing process (windows does not support this feature)
    send_interrupt = false
    # Delay after sending Interrupt signal
    kill_delay = 500 # ms
    # Show log time
    time = false
    # Customize each part's color. If no color found, use the raw app log.
    main = "magenta"
    watcher = "cyan"
    build = "yellow"
    runner = "green"
    # Delete tmp directory on exit
    clean_on_exit = true
    opened by tmwatchanan 9
  • Runtime error: invalid memory address or nil pointer dereference

    Runtime error: invalid memory address or nil pointer dereference

    Constantly getting the following error after any file amend:

    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x4f1aa2]
    goroutine 23 [running]:
    github.com/cosmtrek/air/runner.(*Engine).watching.func1(0xc4200d8780, 0x4b, 0x0, 0x0, 0x553860, 0xc4200d0360, 0x0, 0x0)
            /Users/cosmtrek/Code/go/src/github.com/cosmtrek/air/runner/engine.go:100 +0x32
    path/filepath.walk(0xc420018004, 0x37, 0x5542e0, 0xc4200d3040, 0xc4200de2e0, 0x0, 0x10)
            /usr/local/opt/go/libexec/src/path/filepath/path.go:377 +0x20d
    path/filepath.Walk(0xc420018004, 0x37, 0xc4200de2e0, 0x0, 0xc42003cfc8)
            /usr/local/opt/go/libexec/src/path/filepath/path.go:403 +0x106
    github.com/cosmtrek/air/runner.(*Engine).watching(0xc4200f0000, 0xc420018004, 0x37, 0x0, 0x0)
            /Users/cosmtrek/Code/go/src/github.com/cosmtrek/air/runner/engine.go:98 +0x74
    github.com/cosmtrek/air/runner.(*Engine).watchNewDir.func1(0xc4200f0000, 0xc420018004, 0x37)
            /Users/cosmtrek/Code/go/src/github.com/cosmtrek/air/runner/engine.go:179 +0x43
    created by github.com/cosmtrek/air/runner.(*Engine).watchNewDir
            /Users/cosmtrek/Code/go/src/github.com/cosmtrek/air/runner/engine.go:178 +0x36a

    OS: Windows x64 and Ubuntu 16.04 LTS (Windows subsystem)

    opened by lokhman 9
  • Permission denied after update from 1.12.1 to 1.12.3

    Permission denied after update from 1.12.1 to 1.12.3

    Just updated air from 1.12.1 to 1.12.3. Now running air in go folder fails as follows:

      __    _   ___
     / /\  | | | |_)
    /_/--\ |_| |_| \_ v1.12.3 // live reload for Go apps, with Go1.14.0
    watching .
    !exclude tmp
    /bin/sh: [PATH_TO_FOLDER]/tmp/main: Permission denied
    opened by kasuskasus1 9
  • build from multiple go files

    build from multiple go files

    Using Windows 10 Pro

    I have code in two files: main.go and handlers.go.

    When I build from the command line, no problem: go build -o ./tmp/main.exe. But when I use that same command within .air.conf, it seems it only builds main.go and ignores handlers.go, because I get the error message:

    .\main.go:14:19: undefined: firstFunc
    .\main.go:15:22: undefined: secondFunc

    the exact line in the config file is cmd = "go build -o ./tmp/main.exe"

    How can I run a build within air that includes all the *.go files?

    Note -- this also fails: cmd = "go build -o ./tmp/main.exe main.go handlers.go" However even if this worked, it seems like an unsatisfactory answer, eventually I may have a list of 10, 20, 50 files.

    opened by 2x2xplz 9
  • Fix Relative Path error on Windows

    Fix Relative Path error on Windows

    This error was shown when trying to delete old main.exe file on Windows:

    main.go has changed
    failed to remove D:\code\flutter\instachat\server\tmp\main.exe, error: remove D:\code\flutter\instachat\server\D:\code\flutter\instachat\server\tmp\main.exe: The filename, directory name, or volume label syntax is incorrect.

    This patch fixes that error, locally tested.

    opened by tusharsadhwani 8
  • watch multiple dirs

    watch multiple dirs

    Hey @cosmtrek ,

    Thanks for this awesome tool. Just wondering that is there any support on watching multiple directories atm? I couldn't find any. Or is there any plan to add this feature in the future? Would be really helpful.


    opened by adamleo 7
  • Can not print console log properly

    Can not print console log properly

    System : MacOS 10.14.2 Air version: v1.10

    I try to print in many ways, like this: image

    Start in normal: image It can print all of log.

    Use air to start: image

    The log seems to be printed randomly.

    opened by cyhhao 7
  • Air and command process did not stop by Ctrl+C in the building phase.

    Air and command process did not stop by Ctrl+C in the building phase.

    Air closes a channel e.exitCh when SIGINT is caught (main.go:65). Then e.start() returns (engine.go:306) and e.cleanup() is called (engine.go:91).

    In the e.cleanup(), e.binRunning is checked before a data send to e.binStopCh (engine.go:461). However e.binRunning is false until the command is started (engine.go:411), so no data send to the e.binStopCh in the building phase.

    the building() cannot be stoped, on the next, the command is started after building() succeeded (engine.go:352-366). This running the command could be interrupted by e.buildRunStopCh (engine.go:362), but in this case, no one send data to this channel. After the command started, this command will killed when a data is came from e.binStopCh (engine.go:427). No one send data to the e.binStopCh in this case, so this function wait e.binStopCh forever.

    The cleanup() wait a channel e.canExit (engine.go:484), but e.canExit is not closed while command is running (engine.go:423). Therefore, cleanup() does not return forever.

    As a result, neither the command nor air stopped.

    opened by makiuchi-d 1
  • Feature Request: Add an envirnoment variable section for Air to add them in

    Feature Request: Add an envirnoment variable section for Air to add them in

    Would be super useful so this would work across different OS platforms.

    opened by jherman 0
  • Adding flags to compiled binary adds .exe to the end

    Adding flags to compiled binary adds .exe to the end

    In a Windows environment, using the following in the toml file:

    bin = "tmp\\main.exe --read-envar-file=envar.yaml"

    results in error because air is adding .exe at the very end.

    I don't believe it should be doing this. It should be the responsibility of the user to make sure their bin is proper.

    opened by jherman 1
  • Quote `config.Build.Bin` on the UNIX like OS (including WSL)

    Quote `config.Build.Bin` on the UNIX like OS (including WSL)

    fix #175

    The config.Build.Bin is used as a parameter for /bin/sh -c (runner/util_linux.go:32). It must be quoted because it may contains white space or other spacial chars.

    opened by makiuchi-d 0
  • Use command line flags without config file as alternatives.

    Use command line flags without config file as alternatives.

    The command line is more concise for simple scenarios.

    Like this:

    air --build='./cmd/app' --args='start --config /path/file'
    opened by win5do 3
  • Fixed build and SIGINT race condition and massive sleep after SIGINT

    Fixed build and SIGINT race condition and massive sleep after SIGINT

    This PR addresses two issues. The bigger issue is a race condition that I was seeing when send_interrupt config is true.

    When Air successful builds and runs the binary the e.binRunning is set to true. If then the build fails twice before e.killCmd(...) completes (takes 30 second due to sleeping after sending SIGINT) the build logics sends two messages (e.binRunning is still true) to the binStopCh channel, the 2nd message resulting in a block in the middle of e.withLock, holding the lock until the channel has room. When the attempt to kill the previous running command finishes sleeping, it cannot get the lock to set e.binRunning to false resulting in a dead lock.

    This PR moves the setting of Engine.binRunning to false before trying to interrupt/kill the running process, avoiding the the race condition.

    The 2nd issue is the sleep was being multiplied by time.Milliseconds which was resulting in the duration being multiplied by 1M (see the constants at the bottom of the section at https://golang.org/pkg/time/#pkg-constants).

    I also adjusted some white space and returns in the runner/util_linux.go and runner/util_darwin.go to match.

    opened by ryanolds-drizly 0
  • follow_symlink doesn't work as expected

    follow_symlink doesn't work as expected

    I enabled follow_symlink in .air.toml but it doesn't work. ./vendor/github.com/xxxxx/api-common is a symlink point to /Users/ocavue/go/src/github.com/xxxxx/api-common but air doesn't watch it.

    My shell output:

    $ air 
    [18:31:33] watching vendor/github.com/api-doc-tools
    [18:31:33] watching vendor/github.com/api-doc-tools/swg
    [18:31:33] watching vendor/github.com/api-doc-tools/swg/swagger
    [18:31:33] watching vendor/github.com/xxxxx
    [18:31:33] watching vendor/github.com/xxxxx/go-mysql-driver
    [18:31:33] watching vendor/github.com/xxxxx/thunder
    [18:31:33] watching vendor/github.com/xxxxx/thunder/batch
    [18:31:33] watching vendor/github.com/xxxxx/thunder/concurrencylimiter
    [18:31:33] watching vendor/github.com/xxxxx/thunder/diff
    [18:31:33] watching vendor/github.com/xxxxx/thunder/graphql
    [18:31:33] watching vendor/github.com/xxxxx/thunder/graphql/introspection
    [18:31:33] watching vendor/github.com/xxxxx/thunder/graphql/schemabuilder
    [18:31:33] watching vendor/github.com/xxxxx/thunder/internal
    [18:31:33] watching vendor/github.com/xxxxx/thunder/internal/filter
    [18:31:33] watching vendor/github.com/xxxxx/wiki-api
    [18:31:33] watching vendor/github.com/xxxxx/wiki-api/migration
    [18:31:33] watching vendor/github.com/xxxxx/wiki-api/migration/common
    [18:31:33] watching vendor/github.com/xxxxx/wiki-api/migration/common/uuid
    [18:31:33] watching vendor/github.com/beevik
    [18:31:33] watching vendor/github.com/beevik/etree
    [18:31:33] watching vendor/github.com/bradfitz
    [18:31:33] watching vendor/github.com/bradfitz/slice
    $ ls -lrth vendor/github.com/xxxxx
    drwxr-xr-x  29 ocavue  staff   928B May 24 15:01 go-mysql-driver
    drwxr-xr-x   8 ocavue  staff   256B May 24 15:01 thunder
    drwxr-xr-x   3 ocavue  staff    96B May 24 15:01 wiki-api
    lrwxr-xr-x   1 ocavue  staff    59B Jun 24 16:28 api-common -> /Users/ocavue/go/src/github.com/xxxxx/api-common

    My .air.toml:

    root = "."
    tmp_dir = "tmp"
      bin = "./tmp/main"
      cmd = "go build -o ./tmp/main ."
      delay = 1000
      exclude_dir = ["assets", "tmp", "scripts", "migration"]
      exclude_file = []
      exclude_regex = []
      exclude_unchanged = false
      follow_symlink = true
      full_bin = ""
      include_dir = []
      include_ext = ["go", "tpl", "tmpl", "html"]
      kill_delay = "1s"
      log = "build-errors.log"
      send_interrupt = false
      stop_on_error = true
      app = ""
      build = "yellow"
      main = "magenta"
      runner = "green"
      watcher = "cyan"
      time = true
      clean_on_exit = false
    opened by ocavue 0
  • bug: Can't watch in directories that have spaces in path

    bug: Can't watch in directories that have spaces in path


    I've used air in Windows 10. Works very well, until I tried to run air via WSL. Based on the error message, I deduced that in Linux based systems space are not escaped properly.


    On Windows (works well)

    My path to project: D:\xxx\something with space\project\main.go When running air, it works as expected (watches and recompiles)

    On WSL (didn't work)

    Used Ubuntu for the WSL distro.

    On the same project, on WSL the directory on my machine would be /mnt/d/xxx/something with space/project/main.go When running air (via WSL), it does not work and outputs an error message:

    # ...air output
    /bin/sh: 1:  /mnt/d/xxx/something: not found
    opened by mkamadeus 2
  • Support linux arch64 in installation script.

    Support linux arch64 in installation script.

    This PR adds installation support for linux/arch64

    opened by DrudgeRajen 0
Rick Yu
Read the fucking code \ʕ◔ϖ◔ʔ/
Rick Yu
Realize is the #1 Golang Task Runner which enhance your workflow by automating the most common tasks and using the best performing Golang live reloading.

#1 Golang live reload and task runner Content - ⭐️ Top Features - ???? Get started - ?? Config sample - ?? Commands List - ?? Support and Suggestions

Oxequa 4.1k Jul 16, 2021
Live reload utility for Go web servers

gin gin is a simple command line utility for live-reloading Go web applications. Just run gin in your app directory and your web app will be served wi

Jeremy Saenz 3.7k Jul 19, 2021
EGo lets you build, debug und run Go apps on Intel SGX - as simple as conventional Go programming!

EGo is a framework for building confidential apps in Go. Confidential apps run in always-encrypted and verifiable enclaves on Intel SGX-enabled ha

Edgeless Systems GmbH 148 Jul 26, 2021
Concurrent task runner, developer's routine tasks automation toolkit. Simple modern alternative to GNU Make 🧰

taskctl - concurrent task runner, developer's routine tasks automation toolkit Simple modern alternative to GNU Make. taskctl is concurrent task runne

null 115 Jul 20, 2021
Builds and restarts a Go project when it crashes or some watched file changes

gaper Used to build and restart a Go project when it crashes or some watched file changes Aimed to be used in development only. Changelog See Releases

Max Claus Nunes 49 Jul 1, 2021
🚀 gowatch is a command line tool that builds and (re)starts your go project everytime you save a Go or template file.

gowatch 中文文档 gowatch is a command line tool that builds and (re)starts your go project everytime you save a Go or template file. Installation To insta

silenceper 582 Jul 23, 2021
a build tool for Go, with a focus on cross-compiling, packaging and deployment

goxc NOTE: goxc has long been in maintenance mode. Ever since Go1.5 supported simple cross-compilation, this tool lost much of its value. There are st

Am Laher 1.7k Jul 18, 2021
Build and (re)start go web apps after saving/creating/deleting source files.

unmaintained Fresh Fresh is a command line tool that builds and (re)starts your web application everytime you save a Go or template file. If the web f

Andrea Franz 3.1k Jul 24, 2021
GoReleaser builds Go binaries as fast and easily as possible

GoReleaser builds Go binaries for several platforms, creates a GitHub release and then pushes a Homebrew formula to a tap repository. All that wrapped in your favorite CI.

GoReleaser 8.3k Jul 16, 2021