
Typed environment variables for Go - safe defaults, app env helpers, and zero-ceremony configuration.
Features
env provides strongly-typed access to environment variables with predictable fallbacks. Eliminate string parsing, centralize app environment checks, and keep configuration boring. Designed to feel native to Go - and invisible when things are working.
- Strongly typed getters -
int,bool,float,duration, slices, maps - Safe fallbacks - never panic, never accidentally empty
- Application environment helpers -
local,staging,production - Minimal dependencies - Pure Go, lightweight, minimal surface area
- Framework-agnostic - works with any Go app
- Enum validation - constrain values with allowed sets
- Predictable behavior - no magic, no global state surprises
- Composable building block - ideal for config structs and startup wiring
Why env?
Accessing environment variables in Go often leads to:
- Repeated parsing logic
- Unsafe string conversions
- Inconsistent defaults
- Scattered app environment checks
env solves this by providing typed accessors with fallbacks, so configuration stays boring and predictable.
Installation
go get github.com/goforj/env/v2Quickstart
package main
import (
"log"
"time"
"github.com/goforj/env/v2"
)
func init() {
if err := env.Load(); err != nil {
log.Fatalf("load env: %v", err)
}
}
func main() {
addr := env.Get("ADDR", "127.0.0.1:8080")
debug := env.GetBool("DEBUG", "false")
timeout := env.GetDuration("HTTP_TIMEOUT", "5s")
env.Dump(addr, debug, timeout)
// #string "127.0.0.1:8080"
// #bool false
// #time.Duration 5s
env.Dump("container?", env.IsContainer())
// #string "container?"
// #bool false
}Scoped prefixes
Use Scope when a group of related settings share a common prefix and may also expose named child configs.
package main
import (
"os"
"github.com/goforj/env/v2"
)
func main() {
_ = os.Setenv("STORAGE_DRIVER", "local")
_ = os.Setenv("STORAGE_ROOT", "storage/app/private")
_ = os.Setenv("STORAGE_PUBLIC_DRIVER", "local")
_ = os.Setenv("STORAGE_PUBLIC_ROOT", "storage/app/public")
_ = os.Setenv("STORAGE_AVATARS_DRIVER", "s3")
_ = os.Setenv("STORAGE_AVATARS_BUCKET", "my-bucket")
_ = os.Setenv("STORAGE_AVATARS_REGION", "us-east-1")
storage := env.WithPrefix("STORAGE")
// Root/default config reads STORAGE_* keys.
driver := storage.Get("DRIVER", "local")
root := storage.Get("ROOT", "storage/app/private")
public := storage.Child("PUBLIC")
// Child scopes compose STORAGE_<NAME>_* keys.
publicDriver := public.Get("DRIVER", "local")
publicRoot := public.Get("ROOT", "storage/app/public")
// ChildNames discovers named children while ignoring root keys.
names := storage.ChildNames([]string{
"DRIVER",
"ROOT",
"BUCKET",
"REGION",
})
env.Dump(driver, root, publicDriver, publicRoot, names)
// #string "local"
// #string "storage/app/private"
// #string "local"
// #string "storage/app/public"
// #[]string [
// 0 => "AVATARS" #string
// 1 => "PUBLIC" #string
// ]
}Full kitchen-sink example
See examples/kitchensink/main.go for a runnable program that exercises almost every helper (env loading, typed getters, must-getters, runtime + container detection, and the env.Dump wrapper) with deterministic godump output.
Environment loading
Load loads env files in this order:
.env.env.local,.env.staging, or.env.production, based onAPP_ENV(localby default).env.testingwhen running under tests.env.hostwhen running on the host or DinD
Later files override earlier ones. Repeated calls are no-ops.
Container detection
| Check | True when | Notes |
|---|---|---|
| IsDocker | /.dockerenv or Docker cgroup markers | Generic Docker container |
| IsDockerInDocker | /.dockerenv and docker.sock | Inner DinD container |
| IsDockerHost | docker.sock present, no container cgroups | Host or DinD outer acting as host |
| IsContainer | Any common container signals (Docker, containerd, kube env/cgroup) | General container detection |
| IsKubernetes | KUBERNETES_SERVICE_HOST or kubepods cgroup | Inside a Kubernetes pod |
Runnable examples
Every function has a corresponding runnable example under ./examples.
These examples are generated directly from the documentation blocks of each function, ensuring the docs and code never drift. These are the same examples you see here in the README and GoDoc.
An automated test executes every example to verify it builds and runs successfully.
This guarantees all examples are valid, up-to-date, and remain functional as the API evolves.
Environment file loading
This package uses github.com/joho/godotenv for .env file loading.
It is intentionally composed into the runtime detection and APP_ENV model rather than reimplemented.
Philosophy
env is part of the GoForj toolchain - a collection of focused, composable packages designed to make building Go applications satisfying.
No magic. No globals. No surprises.
API Index
| Group | Functions |
|---|---|
| Application environment | GetAppEnv IsAppEnv IsAppEnvLocal IsAppEnvLocalOrStaging IsAppEnvProduction IsAppEnvStaging IsAppEnvTesting IsAppEnvTestingOrLocal SetAppEnv SetAppEnvLocal SetAppEnvProduction SetAppEnvStaging SetAppEnvTesting |
| Container detection | IsContainer IsDocker IsDockerHost IsDockerInDocker IsHostEnvironment IsKubernetes |
| Debugging | Dump |
| Environment loading | IsEnvLoaded Load LoadEnvFileIfExists Reload |
| Other | Key |
| Runtime | Arch IsBSD IsContainerOS IsLinux IsMac IsUnix IsWindows OS |
| Typed getters | Child ChildNames Get GetBool GetDuration GetEnum GetFloat GetInt GetInt64 GetMap GetMapInt GetSlice GetUint GetUint64 MustGet MustGetBool MustGetInt WithPrefix |
Application environment
GetAppEnv
GetAppEnv returns the current APP_ENV (empty string if unset).
Example: simple retrieval
_ = os.Setenv("APP_ENV", "staging")
env.Dump(env.GetAppEnv())
// #string "staging"IsAppEnv
IsAppEnv checks if APP_ENV matches any of the provided environments.
Example: match any allowed environment
_ = os.Setenv("APP_ENV", "staging")
env.Dump(env.IsAppEnv(env.Production, env.Staging))
// #bool trueExample: unmatched environment
_ = os.Setenv("APP_ENV", "local")
env.Dump(env.IsAppEnv(env.Production, env.Staging))
// #bool falseIsAppEnvLocal
IsAppEnvLocal checks if APP_ENV is "local".
_ = os.Setenv("APP_ENV", env.Local)
env.Dump(env.IsAppEnvLocal())
// #bool trueIsAppEnvLocalOrStaging
IsAppEnvLocalOrStaging checks if APP_ENV is either "local" or "staging".
_ = os.Setenv("APP_ENV", env.Local)
env.Dump(env.IsAppEnvLocalOrStaging())
// #bool trueIsAppEnvProduction
IsAppEnvProduction checks if APP_ENV is "production".
_ = os.Setenv("APP_ENV", env.Production)
env.Dump(env.IsAppEnvProduction())
// #bool trueIsAppEnvStaging
IsAppEnvStaging checks if APP_ENV is "staging".
_ = os.Setenv("APP_ENV", env.Staging)
env.Dump(env.IsAppEnvStaging())
// #bool trueIsAppEnvTesting
IsAppEnvTesting reports whether APP_ENV is "testing" or the process looks like go test.
Example: APP_ENV explicitly testing
_ = os.Setenv("APP_ENV", env.Testing)
env.Dump(env.IsAppEnvTesting())
// #bool trueExample: no test markers
_ = os.Unsetenv("APP_ENV")
env.Dump(env.IsAppEnvTesting())
// #bool false (outside of test binaries)IsAppEnvTestingOrLocal
IsAppEnvTestingOrLocal checks if APP_ENV is "testing" or "local".
_ = os.Setenv("APP_ENV", env.Testing)
env.Dump(env.IsAppEnvTestingOrLocal())
// #bool trueSetAppEnv
SetAppEnv sets APP_ENV to a supported value.
Example: set a supported environment
_ = env.SetAppEnv(env.Staging)
env.Dump(env.GetAppEnv())
// #string "staging"SetAppEnvLocal
SetAppEnvLocal sets APP_ENV to "local".
_ = env.SetAppEnvLocal()
env.Dump(env.GetAppEnv())
// #string "local"SetAppEnvProduction
SetAppEnvProduction sets APP_ENV to "production".
_ = env.SetAppEnvProduction()
env.Dump(env.GetAppEnv())
// #string "production"SetAppEnvStaging
SetAppEnvStaging sets APP_ENV to "staging".
_ = env.SetAppEnvStaging()
env.Dump(env.GetAppEnv())
// #string "staging"SetAppEnvTesting
SetAppEnvTesting sets APP_ENV to "testing".
_ = env.SetAppEnvTesting()
env.Dump(env.GetAppEnv())
// #string "testing"Container detection
IsContainer
IsContainer detects common container runtimes (Docker, containerd, Kubernetes, Podman).
Example: host vs container
env.Dump(env.IsContainer())
// #bool true (inside most containers)
// #bool false (on bare-metal/VM hosts)IsDocker
IsDocker reports whether the current process is running in a Docker container.
Example: typical host
env.Dump(env.IsDocker())
// #bool false (unless inside Docker)IsDockerHost
IsDockerHost reports whether this container behaves like a Docker host.
env.Dump(env.IsDockerHost())
// #bool true (when acting as Docker host)
// #bool false (for normal containers/hosts)IsDockerInDocker
IsDockerInDocker reports whether we are inside a Docker-in-Docker environment.
env.Dump(env.IsDockerInDocker())
// #bool true (inside DinD containers)
// #bool false (on hosts or non-DinD containers)IsHostEnvironment
IsHostEnvironment reports whether the process is running outside any container or orchestrated runtime.
env.Dump(env.IsHostEnvironment())
// #bool true (on bare-metal/VM hosts)
// #bool false (inside containers)IsKubernetes
IsKubernetes reports whether the process is running inside Kubernetes.
env.Dump(env.IsKubernetes())
// #bool true (inside Kubernetes pods)
// #bool false (elsewhere)Debugging
Dump
Dump is a convenience function that calls godump.Dump.
Example: integers
nums := []int{1, 2, 3}
env.Dump(nums)
// #[]int [
// 0 => 1 #int
// 1 => 2 #int
// 2 => 3 #int
// ]Example: multiple values
env.Dump("status", map[string]int{"ok": 1, "fail": 0})
// #string "status"
// #map[string]int [
// "fail" => 0 #int
// "ok" => 1 #int
// ]Environment loading
IsEnvLoaded
IsEnvLoaded reports whether Load or LoadEnvFileIfExists was executed in this process.
env.Dump(env.IsEnvLoaded())
// #bool true (after Load)
// #bool false (otherwise)Load
Load loads .env with optional layering for .env.local/.env.staging/.env.production, plus .env.testing/.env.host when present. It only applies once per process; subsequent calls return without reloading because the result is cached. Use Reload to re-read env files after the first load.
Example: test-specific env file
tmp, _ := os.MkdirTemp("", "envdoc")
_ = os.WriteFile(filepath.Join(tmp, ".env.testing"), []byte("PORT=9090\nENV_DEBUG=0"), 0o644)
_ = os.Chdir(tmp)
_ = os.Setenv("APP_ENV", env.Testing)
_ = env.Load()
env.Dump(os.Getenv("PORT"))
// #string "9090"Example: default .env on a host
_ = os.WriteFile(".env", []byte("SERVICE=api\nENV_DEBUG=3"), 0o644)
_ = env.Load()
env.Dump(os.Getenv("SERVICE"))
// #string "api"LoadEnvFileIfExists
LoadEnvFileIfExists is a compatibility alias for Load.
_ = env.LoadEnvFileIfExists()Reload
Reload re-applies the same layered env loading as Load, even if Load already ran earlier in the same process.
Example: refresh changed env files
_ = os.WriteFile(".env", []byte("SERVICE=api"), 0o644)
_ = env.Load()
_ = os.WriteFile(".env", []byte("SERVICE=worker"), 0o644)
_ = env.Reload()
env.Dump(os.Getenv("SERVICE"))
// #string "worker"Other
Key
Key builds the fully qualified environment key for key within the scope.
Runtime
Arch
Arch returns the CPU architecture the binary is running on.
Example: print GOARCH
env.Dump(env.Arch())
// #string "amd64"
// #string "arm64"IsBSD
IsBSD reports whether the runtime OS is any BSD variant.
env.Dump(env.IsBSD())
// #bool true (on BSD variants)
// #bool false (elsewhere)IsContainerOS
IsContainerOS reports whether this OS is typically used as a container base.
env.Dump(env.IsContainerOS())
// #bool true (on Linux)
// #bool false (on macOS/Windows)IsLinux
IsLinux reports whether the runtime OS is Linux.
env.Dump(env.IsLinux())
// #bool true (on Linux)
// #bool false (on other OSes)IsMac
IsMac reports whether the runtime OS is macOS (Darwin).
env.Dump(env.IsMac())
// #bool true (on macOS)
// #bool false (elsewhere)IsUnix
IsUnix reports whether the OS is Unix-like.
env.Dump(env.IsUnix())
// #bool true (on Unix-like OSes)
// #bool false (e.g., on Windows or Plan 9)IsWindows
IsWindows reports whether the runtime OS is Windows.
env.Dump(env.IsWindows())
// #bool true (on Windows)
// #bool false (elsewhere)OS
OS returns the current operating system identifier.
Example: inspect GOOS
env.Dump(env.OS())
// #string "linux" (on Linux)
// #string "darwin" (on macOS)
// #string "windows" (on Windows)Typed getters
Child
Child returns a new scope rooted at the current prefix plus name.
Example: named child scope
_ = os.Setenv("STORAGE_PUBLIC_ROOT", "storage/app/public")
public := env.WithPrefix("STORAGE").Child("PUBLIC")
env.Dump(
public.Key("ROOT"),
public.Get("ROOT", "storage/app/public"),
)
// #string "STORAGE_PUBLIC_ROOT"
// #string "storage/app/public"ChildNames
ChildNames discovers named child scopes under the current prefix.
Example: discover child names
_ = os.Setenv("STORAGE_DRIVER", "local")
_ = os.Setenv("STORAGE_ROOT", "storage/app/private")
_ = os.Setenv("STORAGE_PUBLIC_ROOT", "storage/app/public")
_ = os.Setenv("STORAGE_AVATARS_BUCKET", "my-bucket")
_ = os.Setenv("STORAGE_AVATARS_REGION", "us-east-1")
names := env.WithPrefix("STORAGE").ChildNames([]string{
"DRIVER",
"ROOT",
"BUCKET",
"REGION",
})
env.Dump(names)
// #[]string [
// 0 => "AVATARS" #string
// 1 => "PUBLIC" #string
// ]Get
Get returns the environment variable for key or fallback when empty.
Example: fallback when unset
os.Unsetenv("DB_HOST")
host := env.Get("DB_HOST", "localhost")
env.Dump(host)
// #string "localhost"Example: prefer existing value
_ = os.Setenv("DB_HOST", "db.internal")
host = env.Get("DB_HOST", "localhost")
env.Dump(host)
// #string "db.internal"GetBool
GetBool parses a boolean from an environment variable or fallback string.
Example: numeric truthy
_ = os.Setenv("DEBUG", "1")
debug := env.GetBool("DEBUG", "false")
env.Dump(debug)
// #bool trueExample: fallback string
os.Unsetenv("DEBUG")
debug = env.GetBool("DEBUG", "false")
env.Dump(debug)
// #bool falseGetDuration
GetDuration parses a Go duration string (e.g. "5s", "10m", "1h").
Example: override request timeout
_ = os.Setenv("HTTP_TIMEOUT", "30s")
timeout := env.GetDuration("HTTP_TIMEOUT", "5s")
env.Dump(timeout)
// #time.Duration 30sExample: fallback when unset
os.Unsetenv("HTTP_TIMEOUT")
timeout = env.GetDuration("HTTP_TIMEOUT", "5s")
env.Dump(timeout)
// #time.Duration 5sGetEnum
GetEnum ensures the environment variable's value is in the allowed list.
Example: accept only staged environments
_ = os.Setenv("APP_ENV", "production")
appEnv := env.GetEnum("APP_ENV", "local", []string{"local", "staging", "production"})
env.Dump(appEnv)
// #string "production"Example: fallback when unset
os.Unsetenv("APP_ENV")
appEnv = env.GetEnum("APP_ENV", "local", []string{"local", "staging", "production"})
env.Dump(appEnv)
// #string "local"GetFloat
GetFloat parses a float64 from an environment variable or fallback string.
Example: override threshold
_ = os.Setenv("THRESHOLD", "0.82")
threshold := env.GetFloat("THRESHOLD", "0.75")
env.Dump(threshold)
// #float64 0.82Example: fallback with decimal string
os.Unsetenv("THRESHOLD")
threshold = env.GetFloat("THRESHOLD", "0.75")
env.Dump(threshold)
// #float64 0.75GetInt
GetInt parses an int from an environment variable or fallback string.
Example: fallback used
os.Unsetenv("PORT")
port := env.GetInt("PORT", "3000")
env.Dump(port)
// #int 3000Example: env overrides fallback
_ = os.Setenv("PORT", "8080")
port = env.GetInt("PORT", "3000")
env.Dump(port)
// #int 8080GetInt64
GetInt64 parses an int64 from an environment variable or fallback string.
Example: parse large numbers safely
_ = os.Setenv("MAX_SIZE", "1048576")
size := env.GetInt64("MAX_SIZE", "512")
env.Dump(size)
// #int64 1048576Example: fallback when unset
os.Unsetenv("MAX_SIZE")
size = env.GetInt64("MAX_SIZE", "512")
env.Dump(size)
// #int64 512GetMap
GetMap parses key=value pairs separated by commas into a map.
Example: parse throttling config
_ = os.Setenv("LIMITS", "read=10, write=5, burst=20")
limits := env.GetMap("LIMITS", "")
env.Dump(limits)
// #map[string]string [
// "burst" => "20" #string
// "read" => "10" #string
// "write" => "5" #string
// ]Example: returns empty map when unset or blank
os.Unsetenv("LIMITS")
limits = env.GetMap("LIMITS", "")
env.Dump(limits)
// #map[string]string []GetMapInt
GetMapInt parses key=int pairs separated by commas into a map. Invalid, missing, or non-positive values fall back to defaultValue.
Example: parse worker queue weights
_ = os.Setenv("QUEUE_WEIGHTS", "critical=6, default=3, low=1")
weights := env.GetMapInt("QUEUE_WEIGHTS", "", 1)
env.Dump(weights)
// #map[string]int [
// "critical" => 6 #int
// "default" => 3 #int
// "low" => 1 #int
// ]Example: invalid values use defaultValue
os.Unsetenv("QUEUE_WEIGHTS")
weights = env.GetMapInt("QUEUE_WEIGHTS", "critical=,default=0,low=nope,misc", 2)
env.Dump(weights)
// #map[string]int [
// "critical" => 2 #int
// "default" => 2 #int
// "low" => 2 #int
// "misc" => 2 #int
// ]GetSlice
GetSlice splits a comma-separated string into a []string with trimming.
Example: trimmed addresses
_ = os.Setenv("PEERS", "10.0.0.1, 10.0.0.2")
peers := env.GetSlice("PEERS", "")
env.Dump(peers)
// #[]string [
// 0 => "10.0.0.1" #string
// 1 => "10.0.0.2" #string
// ]Example: empty becomes empty slice
os.Unsetenv("PEERS")
peers = env.GetSlice("PEERS", "")
env.Dump(peers)
// #[]string []GetUint
GetUint parses a uint from an environment variable or fallback string.
Example: defaults to fallback when missing
os.Unsetenv("WORKERS")
workers := env.GetUint("WORKERS", "4")
env.Dump(workers)
// #uint 4Example: uses provided unsigned value
_ = os.Setenv("WORKERS", "16")
workers = env.GetUint("WORKERS", "4")
env.Dump(workers)
// #uint 16GetUint64
GetUint64 parses a uint64 from an environment variable or fallback string.
Example: high range values
_ = os.Setenv("MAX_ITEMS", "5000")
maxItems := env.GetUint64("MAX_ITEMS", "100")
env.Dump(maxItems)
// #uint64 5000Example: fallback when unset
os.Unsetenv("MAX_ITEMS")
maxItems = env.GetUint64("MAX_ITEMS", "100")
env.Dump(maxItems)
// #uint64 100MustGet
MustGet returns the value of key or panics if missing/empty.
Example: required secret
_ = os.Setenv("API_SECRET", "s3cr3t")
secret := env.MustGet("API_SECRET")
env.Dump(secret)
// #string "s3cr3t"Example: panic on missing value
os.Unsetenv("API_SECRET")
secret = env.MustGet("API_SECRET") // panics: env variable missing: API_SECRETMustGetBool
MustGetBool panics if missing or invalid.
Example: gate features explicitly
_ = os.Setenv("FEATURE_ENABLED", "true")
enabled := env.MustGetBool("FEATURE_ENABLED")
env.Dump(enabled)
// #bool trueExample: panic on invalid value
_ = os.Setenv("FEATURE_ENABLED", "maybe")
_ = env.MustGetBool("FEATURE_ENABLED") // panics when parsingMustGetInt
MustGetInt panics if the value is missing or not an int.
Example: ensure numeric port
_ = os.Setenv("PORT", "8080")
port := env.MustGetInt("PORT")
env.Dump(port)
// #int 8080Example: panic on bad value
_ = os.Setenv("PORT", "not-a-number")
_ = env.MustGetInt("PORT") // panics when parsingWithPrefix
WithPrefix returns a scope rooted at prefix after minimal normalization.
Example: root scope access
_ = os.Setenv("STORAGE_DRIVER", "local")
_ = os.Setenv("STORAGE_ROOT", "storage/app/private")
storage := env.WithPrefix(" STORAGE ")
env.Dump(
storage.Key("DRIVER"),
storage.Get("DRIVER", "s3"),
storage.Get("ROOT", "storage/app/private"),
)
// #string "STORAGE_DRIVER"
// #string "local"
// #string "storage/app/private"License
MIT
