Topics such as go environment variables, environment variables golang, golang environment variables, go env variables, and golang env variables sit on two layers: the Unix-style process environment your binary reads with os.Getenv (often searched as os.getenv golang or golang get environment variable) and os.LookupEnv, and the go env CLI that reports the Go toolchain settings (GOOS, GOMODCACHE, module paths)—what people sometimes mean by golang env or go env alone. This guide covers golang set environment variable with os.Setenv, clearing keys with os.Unsetenv (golang unset variable at the process level), when to prefer LookupEnv, how go env fits in, optional golang env file loading with godotenv, where Viper fits for larger configs, and environment variables best practices. For where Linux sets variables globally, see where to set environment variables in Linux.
Tested with Go 1.24 on Linux.
Read: os.Getenv and os.LookupEnv
os.Getenv returns a string; if the key is missing, it returns "", same as when the key exists but is empty—so you cannot tell those cases apart. os.LookupEnv returns (value string, ok bool); ok is false when the variable is unset.
package main
import (
"fmt"
"os"
)
func main() {
shell := os.Getenv("SHELL")
fmt.Printf("SHELL=%q\n", shell)
if v, ok := os.LookupEnv("HOME"); ok {
fmt.Println("HOME set, length", len(v))
}
}Run locally: you should see SHELL quoted (possibly empty on minimal environments) and a line about HOME when it is set.
Set and unset: os.Setenv / os.Unsetenv
os.Setenv(key, value) updates the current process. os.Unsetenv removes the key. Use os.Clearenv only when you intentionally wipe the environment (rare, security-sensitive).
package main
import (
"fmt"
"os"
)
func main() {
os.Setenv("MYAPP_MODE", "staging")
fmt.Println(os.Getenv("MYAPP_MODE"))
os.Unsetenv("MYAPP_MODE")
fmt.Printf("after unset: %q\n", os.Getenv("MYAPP_MODE"))
}You should see staging then an empty quoted string after unset.
go env (toolchain “golang env”)
The go env command prints Go build configuration, not your whole shell. It answers questions like which GOOS this toolchain targets without importing os in code.
go env GOOS GOARCH GOMODCACHEOn a typical Linux machine you should see three lines: linux, amd64 (or your arch), and a module cache path under your home directory. Values vary by install. Use this when docs say go env variables in the sense of the Go tool, not app-level environment variables golang reads with os.Getenv.
List: os.Environ
os.Environ returns []string entries in the form key=value. Treat it as sensitive: do not log the full slice in production. Prefer known keys or a MYAPP_ prefix filter.
package main
import (
"fmt"
"os"
"strings"
)
func main() {
for _, kv := range os.Environ() {
if strings.HasPrefix(kv, "MYAPP_") {
fmt.Println(kv)
}
}
}With no MYAPP_* variables set, this may print nothing—that is expected.
Optional: golang env file, godotenv, and Viper
For a golang env file (often named .env or app.env), teams keep non-secret defaults beside the repo. Golang get environment variable still flows through the process table: loaders copy file contents into the environment at startup, then you use LookupEnv as usual.
.env layout conventions
- One
KEY=valueper line; uppercase with underscores (for exampleDB_HOST) is a common convention for env-style names. - Put comments on their own lines; avoid trailing inline comments on the same line as a value if your parser is strict.
- Simple values usually need no quotes; if you use quotes, stay consistent with what your loader documents.
Load .env with godotenv
For local development, load a file once with github.com/joho/godotenv. This is not in the standard library—add it to your module and keep real secrets out of git.
go mod init example.com/myapp
go get github.com/joho/godotenvpackage main
import (
"fmt"
"log"
"os"
"github.com/joho/godotenv"
)
func main() {
if err := godotenv.Load(".env"); err != nil {
log.Fatal(err)
}
fmt.Println("PORT=", os.Getenv("PORT"))
}Example .env (dummy values only):
PORT=8080
MYAPP_ENV=devRun locally after creating .env; you should see the printed PORT value.
Viper for layered configuration
When you merge files, flags, and environment overrides, use Viper for structured loading and unmarshaling into structs. For small services, godotenv plus os.LookupEnv is often enough.
Environment variables best practices
- Prefer
LookupEnvfor required keys so “missing” is not confused with empty. - Use one place to load config at startup; pass values down instead of calling
os.Getenvfrom every package. - Do not commit
.envwith secrets; use.env.examplewithout real credentials. - In containers and systemd units, inject config with real env vars or secret mounts rather than baking values into images.
- Separate development and production sources of truth; twelve-factor style config keeps apps portable.
Summary
Go environment variables and golang environment variables at runtime use os.Getenv, os.LookupEnv (for reliable golang get environment variable checks), os.Setenv and os.Unsetenv (for golang set environment variable and clearing), and careful use of os.Environ. Go env and golang env in the toolchain sense mean the go env command, not your app shell. A golang env file does nothing until you load it—godotenv or Viper bridge file content into the process environment or into structs. Follow environment variables best practices for secrets and logging. For app-wide mutable state that is not env-based, see global variables in Go; for reading arbitrary files, see read file into variable.
References
- Go by Example: environment variables
- Package os: Getenv, Setenv, LookupEnv
- go command: environment
- Viper configuration

