How to Use go env to View and Set Environment Variables

Cli
View and set Go environment variables using the go env command to control build paths, proxies, and compiler settings.

The configuration cascade

You install Go on a new machine. You run go mod init. Suddenly your terminal is filled with warnings about GOPATH. You try to set GOPATH in your shell config, but go env still shows the old value. You feel like the tool is lying to you. The truth is go env isn't lying. It's reading from three different places at once, and the shell isn't one of them unless you ask it to be.

go env is the source of truth for the Go toolchain configuration. It merges defaults baked into the binary, persistent overrides stored in a user file, and temporary overrides from your shell environment. When a build fails because Go can't find a module, or when your disk fills up with cached packages, go env is the first place to look. It tells you exactly what the compiler and package manager see, not what you think you configured.

How the hierarchy works

Go resolves environment variables in a strict order. Defaults are hardcoded into the go binary. These are safe, sensible values that work for 90% of users. Next, Go reads the GOENV file. This file lives in ~/.config/go/env on Linux and macOS, or %APPDATA%\go\env on Windows. Changes made with go env -w go here. Finally, Go checks your shell environment variables. Shell variables win over the file, and the file wins over defaults.

This hierarchy is the "ah-ha" moment for many developers. You can override a persistent setting just by exporting a variable in your terminal. This is powerful for testing configurations without committing to them.

Here's the simplest way to check where Go stores your downloaded modules.

# Show the current value of GOMODCACHE to see where modules live
go env GOMODCACHE

The output points to the module cache directory. By default, this is ~/go/pkg/mod. If you see a different path, either you changed it, or your GOPATH is set to something unusual.

Go env shows the truth. Your shell is just a suggestion.

GOPATH versus GOMODCACHE

New users often confuse GOPATH and GOMODCACHE. The names sound similar, and they both point to directories under ~/go by default. They serve different purposes. GOPATH is the workspace root. It's where Go looks for your local source code if you aren't using modules, and it's the base directory for several other caches. GOMODCACHE is specifically where Go stores downloaded modules.

By default, GOMODCACHE lives inside GOPATH. If you change GOPATH, GOMODCACHE moves with it. If you change GOMODCACHE independently, you decouple the module cache from the workspace. This is useful if you want to keep modules on a fast SSD while your code lives elsewhere, or if you want to share a module cache across multiple user accounts.

Here's how to inspect both paths and see their relationship.

# Check the relationship between GOPATH and GOMODCACHE
go env GOPATH GOMODCACHE

If the output shows GOMODCACHE as a subdirectory of GOPATH, they are coupled. If you set GOMODCACHE to a different root, Go uses that path for modules while leaving GOPATH alone.

Don't move GOPATH unless you know what you're doing. The module cache is the heavy lifter; move that first.

Persistent settings with go env -w

The go env -w command writes a key-value pair to the GOENV file. This change persists across terminal sessions and reboots. It's the standard way to configure Go for your user account. You use it to set the module proxy, enable the build cache, or adjust the toolchain behavior.

Here's how to set the module proxy persistently.

# Set GOPROXY persistently so all go commands use the proxy
go env -w GOPROXY=https://proxy.golang.org,direct

The direct fallback tells Go to fetch modules directly from the source if the proxy fails. This is the recommended default for most networks. The command writes to the GOENV file immediately. You don't need to restart your terminal.

Verify the change took effect.

# Verify the change took effect immediately
go env GOPROXY

The output should match what you set. If it doesn't, check for a shell variable override. A stray export GOPROXY=... in your .bashrc or .zshrc will mask the GOENV setting.

Trust the GOENV file. It survives terminal restarts.

Realistic debugging scenario

Imagine your build is failing with a module resolution error. You suspect the proxy is blocked or the cache is corrupted. You need to check the proxy setting, the cache location, and whether the cache is enabled.

Here's a sequence to diagnose the state.

# Check the proxy configuration
go env GOPROXY

# Check if the build cache is enabled and where it lives
go env GOCACHE GOFLAGS

# List all variables to spot unexpected overrides
go env

The GOFLAGS variable is special. It lets you pass build flags via the environment. If GOFLAGS contains -mod=mod, Go forces module mode even if you have a go.mod file that suggests otherwise. This is a common source of confusion. If GOFLAGS is set to something weird, unset it.

# Unset GOFLAGS to restore default build behavior
go env -u GOFLAGS

The -u flag removes the variable from the GOENV file, reverting to the default.

Pitfalls and errors

Using go env is safe, but there are traps. The most common is trying to set a variable that doesn't exist. The Go toolchain validates keys. If you typo a name, the command fails.

# Attempt to set a non-existent variable
go env -w GO_PROXY=https://proxy.golang.org

The compiler rejects this with go env: unknown environment variable GO_PROXY. The correct key is GOPROXY. Always check the official list of environment variables before setting them.

Another pitfall is using go env -w in CI/CD pipelines. CI runners are ephemeral. Writing to the GOENV file is useless if the container dies after the build. In CI, use shell environment variables. They apply to the current run and don't pollute the runner's state.

# Set a variable for the current CI job only
export GONOSUMCHECK=*
go build ./...

The GONOSUMCHECK variable disables checksum verification for specific modules. This is sometimes needed for private modules that aren't in the checksum database. Setting it via shell export ensures it doesn't leak into other jobs.

If you edit the GOENV file manually, you risk breaking the syntax. The file is a simple key=value format. Extra spaces or quotes can cause parse errors.

# output:
go env: malformed GOENV file: invalid syntax

If this happens, open the file in an editor and fix the line. Or delete the file and reset your settings. The defaults are usually fine.

Goroutine leaks happen when the goroutine waits on a channel that never gets closed. Always have a cancellation path. This rule applies to configuration too. If you set a variable that blocks network access, make sure you can unset it.

Advanced variables

Go has variables that control subtle behavior. GOTOOLCHAIN was introduced in Go 1.21. It controls whether Go downloads a specific toolchain version. If your project requires Go 1.22 but you have 1.21 installed, Go can download 1.22 automatically if GOTOOLCHAIN allows it.

# See which toolchain Go will use for builds
go env GOTOOLCHAIN

The default is local, meaning Go uses the installed version without downloading others. Setting it to auto enables automatic downloads. This is useful for team consistency. Everyone gets the same compiler version regardless of their local install.

GOEXPERIMENT enables experimental features. These are not stable. They can change or disappear in future releases. Use them only if you need a specific feature and accept the risk.

# Enable an experimental feature temporarily
export GOEXPERIMENT=loopvar
go build ./...

The loopvar experiment changes how loop variables are captured in goroutines. Go 1.22 made this change permanent, so the experiment is no longer needed. go env helps you see which experiments are active.

Convention aside: GOFLAGS is a convention for passing flags via env. The community accepts this pattern because it reduces repetition. If you always build with -race, set GOFLAGS=-race in your shell. Just remember to unset it when you need a clean build.

Decision matrix

Use go env to inspect the active configuration before debugging a build. Use go env -w to set a persistent preference for your development machine. Use shell export to override settings temporarily for a single session or script. Use go env -u to remove a custom setting and restore the default behavior. Use GOFLAGS to pass build flags globally without repeating them on the command line. Use GOMODCACHE instead of GOPATH when you only need to move the module cache. Use GOTOOLCHAIN to enforce version consistency across a team. Use plain sequential code when you don't need concurrency: the simplest thing that works is usually the right thing.

Where to go next