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 projects
  • pkg/ — compiled package objects, cached per architecture
  • bin/ — compiled binaries from go 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 where go install puts 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.

1

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.

2

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.

3

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.