Key Environment Variables in Go
A complete reference for GOROOT, GOPATH, GOBIN, GOOS, GOARCH, GOPROXY, GOSUMDB, GOPRIVATE, GO111MODULE, GOCACHE, and GOMODCACHE — what they do, when to set them, and how they shape the Go toolchain.
The Go toolchain uses a set of environment variables to locate its own installation, manage your workspace, control compilation, and decide how modules are fetched. A solid understanding of these variables eliminates a large class of “works on my machine” problems and makes cross‑compilation and private module handling predictable.
Below we cover the variables that every Go developer encounters, listed in the order they tend to matter as you move from a first install to production builds.
GOROOT — the Go installation root
GOROOT tells the toolchain where Go itself is installed. It points to the directory containing the bin/, src/, and pkg/ directories of the Go distribution.
In nearly all installations, you do not need to set GOROOT manually. The go binary discovers its location by examining its own executable path. The value is available through go env GOROOT:
go env GOROOT
# Typical output on Linux: /usr/local/go
# On macOS with Homebrew: /usr/local/Cellar/go/1.22.0/libexec
You only set GOROOT when you have installed Go in a non‑standard location and the tool cannot find it — for example, after extracting a tarball to a custom path. In that situation, export the variable in your shell profile:
export GOROOT=/opt/go-custom
export PATH=$GOROOT/bin:$PATH
Setting GOROOT incorrectly breaks everything:
If GOROOT points to a directory that does not contain a valid Go installation, commands like go build will fail with cryptic errors about missing packages. The most common trigger is a stale GOROOT left over from a previous install. Before tweaking GOROOT, run go env GOROOT and verify that the path shown matches where Go really lives on your system.
A common beginner mistake is setting GOROOT to the user’s home directory or to the old GOPATH because a tutorial from 2014 said to. Today, the toolchain handles this automatically — leave it alone unless you know exactly why you need to override it.
GOPATH — the classic workspace
GOPATH defined the center of Go development before modules arrived. It specifies a workspace directory where the toolchain stores source code, compiled packages, and installed binaries. Its default is $HOME/go on Unix and %USERPROFILE%\go on Windows.
Inside GOPATH, three subdirectories served distinct purposes:
src/— the source of Go packages and projectspkg/— compiled package objects, cached per architecturebin/— compiled binaries fromgo install
In the module era, you no longer need to place your projects inside $GOPATH/src. The go command uses GOPATH mainly for two things:
- The
bin/directory wherego installputs compiled binaries (unless GOBIN is set). - The module download cache stored at
$GOPATH/pkg/mod.
You can inspect the current value with:
go env GOPATH
# /home/yourname/go
Even with modules, GOPATH’s bin/ matters because it is automatically added to the default GOBIN location. If you want your installed Go tools to be callable from anywhere, you must include $GOPATH/bin in your PATH:
export PATH=$PATH:$(go env GOPATH)/bin
Don't set GOPATH to your Go installation directory:
GOPATH and GOROOT must never point to the same location. Mixing them causes build failures and impossible‑to‑debug package resolution errors. If you have ever run export GOPATH=/usr/local/go, undo it now.
GOBIN — where compiled binaries land
When you run go install, the toolchain compiles the main package and copies the resulting binary to a well‑known directory. GOBIN overrides that directory. If GOBIN is not set, the binary goes to $GOPATH/bin.
Setting a custom GOBIN is useful when you want to keep Go‑built tools separate from other binaries, or when you work in an environment where $GOPATH/bin is not convenient. For example, to place all Go tools directly in $HOME/bin:
export GOBIN=$HOME/bin
export PATH=$PATH:$GOBIN
To verify:
go env GOBIN
# /home/yourname/bin
After building a tool like golangci-lint with go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest, the binary appears in that directory.
Check that GOBIN is on your PATH:
After installing a tool, run which golangci-lint (or where.exe on Windows). If the shell cannot find it, confirm that your GOBIN is listed in echo $PATH. Adding the GOBIN export to .bashrc, .zshrc, or your PowerShell profile makes the setting persistent.
GOOS and GOARCH — cross‑compilation targets
Go compiles to many operating systems and architectures from a single development machine. The variables GOOS (target operating system) and GOARCH (target architecture) control what platform the resulting binary runs on.
By default, they match your current system. You can see their values:
go env GOOS GOARCH
# linux amd64
To build a binary for a different platform, set them inline:
GOOS=windows GOARCH=amd64 go build -o hello.exe main.go
GOOS=linux GOARCH=arm64 go build -o hello-linux-arm64 main.go
The valid combinations are extensive. Common ones include linux/amd64, darwin/amd64, darwin/arm64, windows/amd64, linux/arm64. You can list all supported pairs with go tool dist list.
Cross‑compilation requires no extra toolchain:
Go's compiler can produce binaries for any supported platform without installing additional libraries or SDKs. The standard library source is already available in your GOROOT. The only exception is when CGO is involved; pure Go programs cross‑compile with zero friction.
A frequent beginner mistake is forgetting that GOOS and GOARCH do not change the current system’s environment — they only affect the binary produced by go build or go test. Running GOOS=linux go run main.go won’t execute the program on Linux; it will still run on your local OS but compile the binary for Linux and then attempt to execute it, which will fail.
GOPROXY, GOSUMDB, and GOPRIVATE — module download control
When Go modules fetch dependencies, they do so through a module proxy and verify checksums against a public database. These three variables govern that process.
GOPROXY
GOPROXY is a comma‑separated list of module proxy URLs. The default is https://proxy.golang.org,direct. The go command first asks the proxy for a module; if the proxy doesn’t have it, it falls back to direct, which means fetching from the version control system (GitHub, GitLab, etc.).
You might override it to use a corporate proxy or to skip proxies entirely:
# Use only the public proxy
go env -w GOPROXY=https://proxy.golang.org
# Skip the proxy and go straight to source
go env -w GOPROXY=direct
# Use a self‑hosted proxy first, then the public one
go env -w GOPROXY=https://my-company-proxy.example.com,https://proxy.golang.org,direct
GOSUMDB
GOSUMDB points to a checksum database that provides tamper‑proof hashes of module content. Its default is sum.golang.org followed by the public key. When you download a module, Go verifies its hash against this database to ensure it hasn’t been modified.
Most developers never need to change GOSUMDB. You would set it to off only in isolated air‑gapped environments or when using a private proxy that handles verification itself. Setting GOSUMDB to off disables the security check entirely.
GOPRIVATE
GOPRIVATE is a glob pattern that identifies private modules — modules that should not be fetched through the public proxy or checked against the public checksum database. It acts as a shortcut: setting GOPRIVATE automatically sets the internal variables GONOSUMDB, GONOSUMCHECK, and GONOPROXY to the same pattern.
go env -w GOPRIVATE=github.com/mycompany/*,gitlab.internal.net/*
With this setting, any module matching those patterns bypasses the proxy and the sum database, and instead fetches directly from your version control system. You still need to have the appropriate credentials configured for git or your private host.
Public proxy leakage:
Without GOPRIVATE, every request for a private module hits the public proxy proxy.golang.org. The proxy cannot serve it, but the request itself reveals your internal package names. Always set GOPRIVATE before building a project that imports private modules.
GO111MODULE — toggling module mode
GO111MODULE controls whether the go command runs in module‑aware mode. It has three possible values: on, off, and auto. In Go 1.16 and later, the default is on, and off is no longer supported. The auto mode behaved as on when a go.mod file was present and off otherwise.
This variable exists primarily for backward compatibility. If you are starting a new project with a recent Go version, you can ignore GO111MODULE entirely — the toolchain defaults to the only supported behavior. You may encounter it in legacy documentation or older CI scripts, where it is set to on as a safeguard.
Modern Go assumes modules:
Go 1.18 and newer no longer support GO111MODULE=off. If a script sets this value, the build will fail with an error. Remove any such assignment from your environment.
GOCACHE and GOMODCACHE — build and module caching
Go caches compiled packages and downloaded modules to speed up subsequent builds. Two separate caches manage these:
- GOCACHE — the build cache, holding compiled packages and test results.
- GOMODCACHE — the module cache, storing downloaded module source code.
On Unix, the defaults are $HOME/.cache/go-build and $GOPATH/pkg/mod respectively. You can inspect them:
go env GOCACHE GOMODCACHE
When to touch the cache
The caches are self‑managing; you rarely need to interact with them. However, in a few situations you may want to clear them:
- After a Go toolchain upgrade, to force recompilation of everything.
- When a corrupted cached package causes a bizarre build error.
- When you want to reclaim disk space on a CI machine.
Use the dedicated go clean commands rather than deleting the directories manually:
go clean -cache # clears GOCACHE
go clean -modcache # clears GOMODCACHE
go clean -cache -modcache # clears both
Don't delete the cache directories manually:
While manually removing $GOCACHE or $GOMODCACHE will work, the go clean commands are safer and leave any locks or metadata intact. After clearing the module cache, the next build will re‑download all dependencies, which can be slow on a weak connection.
Setting custom cache locations is useful on systems where the home directory has limited space or is on a slow filesystem:
export GOCACHE=/fast-ssd/go-build-cache
go env -w GOCACHE=/fast-ssd/go-build-cache
How to inspect and set environment variables
Go provides a unified way to view and persist environment variable changes.
Inspecting variables
List every Go‑related variable with:
go env
To see a specific one:
go env GOPATH
Setting variables temporarily
For a single command, prefix it with the variable assignments:
GOPROXY=direct go build ./...
Setting variables permanently (Go 1.13+)
The go env -w command writes the setting to the Go‑specific environment file (located at os.UserConfigDir()/go/env). Changes persist across shell sessions and do not require modifying your .bashrc or .zshrc.
Step 1: Identify the variable and desired value
Decide which variable you need to set and what value it should have. For example, to send all module requests through a corporate proxy, you would set GOPROXY=https://corp-proxy.example.com.
Step 2: Use go env -w
Run the command with the key‑value pair:
go env -w GOPROXY=https://corp-proxy.example.com,direct
The tool writes the change and confirms nothing. Multiple variables can be set in one command by listing them sequentially.
Step 3: Confirm the change
Verify that the variable now reflects your new value:
go env GOPROXY
# https://corp-proxy.example.com,direct
The change is immediate and visible to all subsequent go commands.
go env -w keeps things clean:
Using go env -w instead of shell exports keeps Go configuration self‑contained. You avoid cluttering your shell profile and reduce the risk of conflicting values between different Go projects that might need different settings. If you ever need to undo a change, go env -u VARNAME removes the custom setting and falls back to the default.
Setting variables in different shells
For commands that must be available to non‑Go tools or for older Go versions, use shell exports. The syntax varies.
export GOBIN=$HOME/bin
export PATH=$PATH:$GOBIN
Add these lines to ~/.bashrc or ~/.zshrc and run source ~/.bashrc to activate them.
Common pitfalls and their fixes
Even experienced developers occasionally stumble over environment variable misconfigurations. The most frequent problems fall into three buckets.
GOROOT set when it shouldn’t be. A leftover GOROOT from a previous install can cause “package not found” errors for standard library packages. Fix: unset GOROOT and let the toolchain auto‑detect.
GOPATH/bin missing from PATH. After installing a tool like staticcheck, the binary exists but the shell can’t find it. Fix: add $(go env GOPATH)/bin to your PATH and restart your shell.
GOPRIVATE forgotten for private modules. A team builds a project that uses github.com/mycompany/secrets and go build fails with a 404 or a checksum mismatch. Fix: set go env -w GOPRIVATE=github.com/mycompany/* and ensure git has valid credentials.
Cross‑compilation with CGO_ENABLED=0 unexpectedly. Some Go code uses CGO implicitly (e.g., the net package on macOS). If you cross‑compile and CGO is disabled, the build might succeed but produce a binary that behaves differently. Check go env CGO_ENABLED before cross‑compiling and, for pure Go programs, set CGO_ENABLED=0 to guarantee a static binary.
Summary
The environment variables covered here form the control surface between you and the Go toolchain. GOROOT and GOPATH anchor the installation and workspace; GOBIN decides where your compiled tools land; GOOS and GOARCH unlock cross‑compilation; GOPROXY, GOSUMDB, and GOPRIVATE govern module security and privacy; and GOCACHE and GOMODCACHE keep builds fast.
The most important habit to build: whenever you encounter an unexpected behavior — a missing binary, a failed download, a mysterious compilation error — run go env first. The output often pinpoints the misconfigured variable before you waste time debugging code.