Core Build Commands

An in-depth look at go build, go run, go install, and go clean — the essential commands that compile, execute, install, and clean Go code.

Four subcommands cover the majority of day-to-day Go building and execution: go build, go run, go install, and go clean. Each one answers a different question — Do my sources compile? Can I see my program run right now? Should this tool be available everywhere on my system? Do I need a truly fresh build? — and knowing which one to reach for eliminates guesswork and keeps your workflow fast.

go build

go build compiles Go source files and their dependencies into an executable or, for non‑main packages, verifies that they can be built without producing a permanent output file. It is the primary way to check compilation and create a binary you can run manually.

The command existed from the earliest days of the go tool precisely because Go’s design insists that source code alone must be enough to drive the build. No Makefiles, no XML configuration — the import statements in your .go files tell the compiler everything it needs. go build exists to make that idea practical: it resolves imports, finds the packages on disk (or downloads them in module mode), compiles them, and links the result, all without you writing a single build rule.

When you run go build inside a directory that is a main package, the default behaviour is to produce an executable named after the directory (or, if you pass a list of .go files, after the first file). On Windows a .exe suffix is added. For non‑main packages — like libraries — go build compiles the code but discards the object file; it acts purely as a “does this compile?” check.

// main.go — a minimal program
package main
import "fmt"
func main() {
    fmt.Println("hello")
}
# In the same directory:
go build

After this, an executable named after the directory (for example, hello if the folder is called hello) appears in the current working directory. Nothing is installed elsewhere. If the folder is not a main package, go build succeeds silently but leaves no file behind — it just confirms the code is valid.

The -o flag lets you name the output or place it in a different location. This is indispensable when you need multiple binaries from the same source or want to avoid name collisions.

go build -o myapp main.go

Now the file myapp exists, regardless of what the directory or first source file was called.

Multiple main packages and go build ./...:

Running go build ./... in a project root that contains several main packages causes each executable to land in the current directory. Because the binaries are named after their directories, two main packages in the same directory tree with the same directory name will overwrite each other without warning. Prefer explicit package paths when building commands.

How go build finds dependencies

In module‑aware mode (any directory with a go.mod file), go build automatically downloads missing dependencies into the module cache and updates go.mod with // indirect requirements if needed. This means you can clone a repository, run go build, and have everything fetched — no separate go get step required for modules listed in go.mod. In the older GOPATH mode, the packages must already exist under $GOPATH/src, and go build will not fetch anything.

This behaviour reinforces the convention‑over‑configuration philosophy: the module path, the source layout, and the import statements are sufficient for the tool to locate code.

Common mistakes

  • Expecting go build to install anything. It only produces an executable in the working directory (or discards it for libraries). Use go install to place binaries in a standard bin directory.
  • Forgetting the -o flag with multiple source files. Without -o, the binary name is derived from the first .go file listed, which often surprises.
  • Running go build in a directory that is not inside a module and not inside GOPATH. The tool will complain about missing go.mod. Either initialise a module (go mod init) or work inside $GOPATH/src.

go run

go run compiles a main package and executes the resulting binary in one step, then cleans up the temporary file. It exists because the write‑compile‑run loop is tedious: after every small edit you want to see the result immediately, not accumulate a pile of stale binaries in your working directory.

Under the hood, go run does the same compilation work as go build, but it writes the executable to a temporary directory (usually inside the system’s temp folder), runs it, and removes it when the process exits. This makes it perfect for one‑off experiments, short scripts, or the rapid iteration phase of a new feature.

go run main.go

Any extra arguments after the file name are forwarded to the program, not to the go tool:

go run main.go --port 8080

go run is not for production:

The compilation step runs every time you invoke go run, so startup is measurably slower than launching a pre‑built binary. For long‑lived services or frequent restarts, prefer go build and run the resulting executable.

Flags

go run accepts many of the same build flags as go build, including -race for data race detection and -gcflags for compiler tuning. Pass them before the source file name:

go run -race main.go

The -x flag prints the compile and link commands that go run executes internally, which is a good way to understand what the tool is doing when something goes wrong.

When the program exits

The temporary binary is deleted as soon as the program finishes. If you kill the process with Ctrl+C, Go still attempts to remove the temporary file. On Unix systems the cleanup is reliable; on Windows, locked files may occasionally linger until the next reboot.

A common beginner expectation is that go run leaves a reusable binary. It does not. For a binary you want to keep or distribute, you must use go build or go install.

go install

go install compiles a package and then installs the result — an executable for a main package, or a cached object file for a library — into the designated Go directories. Its purpose is to make Go‑built tools available system‑wide, without cluttering your project folders.

For a main package, go install places the executable in $GOPATH/bin (or $GOBIN if set). Once that directory is on your PATH, you can invoke the command from any terminal. For non‑main packages, the compiled object is stored in the build cache so that future builds of other packages can reuse it.

go install

If the current directory is a main package inside a module, this will compile the module and drop the resulting binary into $GOPATH/bin. The binary’s name is determined by the last segment of the module path — not the directory name — so a module example.com/myapp produces myapp.

Verify the installation:

After go install, check with which myapp (Unix) or where myapp (Windows). If the tool’s bin directory is on your PATH, the shell will locate the freshly installed binary immediately.

The distinction between go build and go install

Before Go 1.10, go install would also install any out‑of‑date dependencies. That caused confusion — developers expected go install mycmd to install only mycmd. Now, go install installs exactly what you ask for and leaves dependencies in the build cache. This matches the mental model of “build” versus “install and make available”.

A practical rule of thumb:

  • Use go build when you need a local binary to test or run manually, and you don’t care where it lives.
  • Use go install when you want the binary to become a permanent tool on your system, accessible from anywhere.

Installing a specific version from a remote module

Since Go 1.16, you can install a command directly from its module path and version without cloning the repository:

go install golang.org/x/tools/cmd/goimports@latest

This downloads the module, compiles the main package at cmd/goimports, and places the binary in $GOPATH/bin. It does not add the dependency to your current module’s go.mod. This is the simplest way to obtain development tools like static analysers and code generators.

Stale binaries and go install with -a:

When you use go install -a to force rebuilding all dependencies, only the named command gets installed — the rebuilt dependencies are just cached. If your goal is to refresh every installed binary, you must install each one explicitly or use a script. Assuming that -a reinstalls everything is a common source of stale‑binary bugs.

go clean

go clean removes object files, cached test results, and other build artifacts. It exists because the Go toolchain keeps compiled packages in a build cache for speed, and sometimes you need to clear that cache — whether to reclaim disk space, force a genuine rebuild, or eliminate a stale test result that go test refuses to invalidate.

With no flags, go clean removes the object files associated with the current package. In module mode there are no persistent object files in the source tree, so the default behaviour has a limited effect. The real power is in its flags.

Core flags

FlagEffect
-cachePurges the entire build cache (GOCACHE directory). The next build must recompile every package from source.
-testcacheExpires all cached test results. Useful when tests pass locally but fail in CI, and you suspect a cached pass is hiding a failure.
-modcacheRemoves the entire module download cache (GOMODCACHE). The next build re‑downloads all dependencies.
-iRemoves the corresponding installed binary (from $GOPATH/bin). This keeps the bin directory clean when you stop using a tool.
-nPrints what would be removed without actually deleting anything. Always try -n first.

go clean -cache slows the next build:

After go clean -cache, every package in your project and every standard library package it imports must be recompiled. This can turn a sub‑second incremental build into a multi‑minute wait. Only clear the cache when you have a specific reason — stale builds, disk pressure, or debugging a toolchain issue.

A practical workflow

If a test that was previously passing suddenly starts failing, and you haven’t changed the test or the code it exercises, a stale cached test result could be the culprit. Run go clean -testcache and then go test ./... again. This forces the test to run from scratch without touching the build cache, so compilation remains fast while the test output is guaranteed fresh.

Summary

go build, go run, go install, and go clean form a minimal, no‑ceremony pipeline that replaces Makefiles and build scripts for the vast majority of Go projects. The crucial distinction is intent: build verifies and produces a binary where you stand; run gives you an immediate, disposable execution; install makes the result a permanent system tool; clean resets the state when the toolchain’s caching optimisations get in your way.

If you are writing a service that will run on a server, the natural cycle is go build (or go install) to produce the binary, then orchestrate it with a process manager. If you are writing a short script, go run is the right call. If you are fetching a static analysis tool, go install tool@latest is the one‑liner. Knowing which subcommand matches which goal lets you work with the tool rather than fight it.