https://github.com/goforj/env
Strongly-typed environment variables for Go with safe defaults, app env helpers, and zero-ceremony configuration.
https://github.com/goforj/env
configuration developer-experience dotenv env environment-variables go goforj golang typed-config
Last synced: about 2 months ago
JSON representation
Strongly-typed environment variables for Go with safe defaults, app env helpers, and zero-ceremony configuration.
- Host: GitHub
- URL: https://github.com/goforj/env
- Owner: goforj
- License: mit
- Created: 2025-12-15T20:35:19.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-01-14T03:55:41.000Z (about 2 months ago)
- Last Synced: 2026-01-16T20:10:48.607Z (about 2 months ago)
- Topics: configuration, developer-experience, dotenv, env, environment-variables, go, goforj, golang, typed-config
- Language: Go
- Homepage:
- Size: 13 MB
- Stars: 3
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Typed environment variables for Go - safe defaults, app env helpers, and zero-ceremony configuration.
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.
# Features
- **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.
## Features
- Strongly typed getters (`int`, `bool`, `duration`, slices, maps)
- Safe fallbacks (never panic, never empty by accident)
- App environment helpers (`local`, `staging`, `production`)
- Zero dependencies
- Framework-agnostic
## Installation
```bash
go get github.com/goforj/env/v2
````
## Quickstart
```go
package main
import (
"log"
"time"
"github.com/goforj/env/v2"
)
func init() {
if err := env.LoadEnvFileIfExists(); 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
}
```
### Full kitchen-sink example
See [examples/kitchensink/main.go](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 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.
## Runnable examples
Every function has a corresponding runnable example under [`./examples`](./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.
## Container detection at a glance
| 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 |
## Environment loading overview
LoadEnvFileIfExists layers env files in a predictable order:
- `.env` is loaded first.
- `.env.local`, `.env.staging`, or `.env.production` overlays based on `APP_ENV` (defaults to `local` when unset).
- `.env.testing` overlays when running under tests (APP_ENV=testing or Go test markers).
- `.env.host` overlays when running on the host or DinD to support host-to-container networking.
Later files override earlier values. Subsequent calls are no-ops.
### Index
| Group | Functions |
|------:|-----------|
| **Application environment** | [GetAppEnv](#getappenv) [IsAppEnv](#isappenv) [IsAppEnvLocal](#isappenvlocal) [IsAppEnvLocalOrStaging](#isappenvlocalorstaging) [IsAppEnvProduction](#isappenvproduction) [IsAppEnvStaging](#isappenvstaging) [IsAppEnvTesting](#isappenvtesting) [IsAppEnvTestingOrLocal](#isappenvtestingorlocal) [SetAppEnv](#setappenv) [SetAppEnvLocal](#setappenvlocal) [SetAppEnvProduction](#setappenvproduction) [SetAppEnvStaging](#setappenvstaging) [SetAppEnvTesting](#setappenvtesting) |
| **Container detection** | [IsContainer](#iscontainer) [IsDocker](#isdocker) [IsDockerHost](#isdockerhost) [IsDockerInDocker](#isdockerindocker) [IsHostEnvironment](#ishostenvironment) [IsKubernetes](#iskubernetes) |
| **Debugging** | [Dump](#dump) |
| **Environment loading** | [IsEnvLoaded](#isenvloaded) [LoadEnvFileIfExists](#loadenvfileifexists) |
| **Runtime** | [Arch](#arch) [IsBSD](#isbsd) [IsContainerOS](#iscontaineros) [IsLinux](#islinux) [IsMac](#ismac) [IsUnix](#isunix) [IsWindows](#iswindows) [OS](#os) |
| **Typed getters** | [Get](#get) [GetBool](#getbool) [GetDuration](#getduration) [GetEnum](#getenum) [GetFloat](#getfloat) [GetInt](#getint) [GetInt64](#getint64) [GetMap](#getmap) [GetSlice](#getslice) [GetUint](#getuint) [GetUint64](#getuint64) [MustGet](#mustget) [MustGetBool](#mustgetbool) [MustGetInt](#mustgetint) |
## Application environment
GetAppEnv returns the current APP_ENV (empty string if unset).
_Example: simple retrieval_
```go
_ = os.Setenv("APP_ENV", "staging")
env.Dump(env.GetAppEnv())
// #string "staging"
```
IsAppEnv checks if APP_ENV matches any of the provided environments.
_Example: match any allowed environment_
```go
_ = os.Setenv("APP_ENV", "staging")
env.Dump(env.IsAppEnv(env.Production, env.Staging))
// #bool true
```
_Example: unmatched environment_
```go
_ = os.Setenv("APP_ENV", "local")
env.Dump(env.IsAppEnv(env.Production, env.Staging))
// #bool false
```
IsAppEnvLocal checks if APP_ENV is "local".
```go
_ = os.Setenv("APP_ENV", env.Local)
env.Dump(env.IsAppEnvLocal())
// #bool true
```
### IsAppEnvLocalOrStaging · readonly
IsAppEnvLocalOrStaging checks if APP_ENV is either "local" or "staging".
```go
_ = os.Setenv("APP_ENV", env.Local)
env.Dump(env.IsAppEnvLocalOrStaging())
// #bool true
```
### IsAppEnvProduction · readonly
IsAppEnvProduction checks if APP_ENV is "production".
```go
_ = os.Setenv("APP_ENV", env.Production)
env.Dump(env.IsAppEnvProduction())
// #bool true
```
### IsAppEnvStaging · readonly
IsAppEnvStaging checks if APP_ENV is "staging".
```go
_ = os.Setenv("APP_ENV", env.Staging)
env.Dump(env.IsAppEnvStaging())
// #bool true
```
### IsAppEnvTesting · readonly
IsAppEnvTesting reports whether APP_ENV is "testing" or the process looks like `go test`.
_Example: APP_ENV explicitly testing_
```go
_ = os.Setenv("APP_ENV", env.Testing)
env.Dump(env.IsAppEnvTesting())
// #bool true
```
_Example: no test markers_
```go
_ = os.Unsetenv("APP_ENV")
env.Dump(env.IsAppEnvTesting())
// #bool false (outside of test binaries)
```
### IsAppEnvTestingOrLocal · readonly
IsAppEnvTestingOrLocal checks if APP_ENV is "testing" or "local".
```go
_ = os.Setenv("APP_ENV", env.Testing)
env.Dump(env.IsAppEnvTestingOrLocal())
// #bool true
```
### SetAppEnv · mutates-process-env
SetAppEnv sets APP_ENV to a supported value.
_Example: set a supported environment_
```go
_ = env.SetAppEnv(env.Staging)
env.Dump(env.GetAppEnv())
// #string "staging"
```
### SetAppEnvLocal · mutates-process-env
SetAppEnvLocal sets APP_ENV to "local".
```go
_ = env.SetAppEnvLocal()
env.Dump(env.GetAppEnv())
// #string "local"
```
### SetAppEnvProduction · mutates-process-env
SetAppEnvProduction sets APP_ENV to "production".
```go
_ = env.SetAppEnvProduction()
env.Dump(env.GetAppEnv())
// #string "production"
```
### SetAppEnvStaging · mutates-process-env
SetAppEnvStaging sets APP_ENV to "staging".
```go
_ = env.SetAppEnvStaging()
env.Dump(env.GetAppEnv())
// #string "staging"
```
### SetAppEnvTesting · mutates-process-env
SetAppEnvTesting sets APP_ENV to "testing".
```go
_ = env.SetAppEnvTesting()
env.Dump(env.GetAppEnv())
// #string "testing"
```
## Container detection
IsContainer detects common container runtimes (Docker, containerd, Kubernetes, Podman).
_Example: host vs container_
```go
env.Dump(env.IsContainer())
// #bool true (inside most containers)
// #bool false (on bare-metal/VM hosts)
```
IsDocker reports whether the current process is running in a Docker container.
_Example: typical host_
```go
env.Dump(env.IsDocker())
// #bool false (unless inside Docker)
```
IsDockerHost reports whether this container behaves like a Docker host.
```go
env.Dump(env.IsDockerHost())
// #bool true (when acting as Docker host)
// #bool false (for normal containers/hosts)
```
### IsDockerInDocker · readonly
IsDockerInDocker reports whether we are inside a Docker-in-Docker environment.
```go
env.Dump(env.IsDockerInDocker())
// #bool true (inside DinD containers)
// #bool false (on hosts or non-DinD containers)
```
### IsHostEnvironment · readonly
IsHostEnvironment reports whether the process is running *outside* any
container or orchestrated runtime.
```go
env.Dump(env.IsHostEnvironment())
// #bool true (on bare-metal/VM hosts)
// #bool false (inside containers)
```
IsKubernetes reports whether the process is running inside Kubernetes.
```go
env.Dump(env.IsKubernetes())
// #bool true (inside Kubernetes pods)
// #bool false (elsewhere)
```
## Debugging
Dump is a convenience function that calls godump.Dump.
_Example: integers_
```go
nums := []int{1, 2, 3}
env.Dump(nums)
// #[]int [
// 0 => 1 #int
// 1 => 2 #int
// 2 => 3 #int
// ]
```
_Example: multiple values_
```go
env.Dump("status", map[string]int{"ok": 1, "fail": 0})
// #string "status"
// #map[string]int [
// "fail" => 0 #int
// "ok" => 1 #int
// ]
```
## Environment loading
IsEnvLoaded reports whether LoadEnvFileIfExists was executed in this process.
```go
env.Dump(env.IsEnvLoaded())
// #bool true (after LoadEnvFileIfExists)
// #bool false (otherwise)
```
### LoadEnvFileIfExists · mutates-process-env
LoadEnvFileIfExists loads .env with optional layering for .env.local/.env.staging/.env.production,
plus .env.testing/.env.host when present.
_Example: test-specific env file_
```go
tmp, _ := os.MkdirTemp("", "envdoc")
_ = os.WriteFile(filepath.Join(tmp, ".env.testing"), []byte("PORT=9090\nAPP_DEBUG=0"), 0o644)
_ = os.Chdir(tmp)
_ = os.Setenv("APP_ENV", env.Testing)
_ = env.LoadEnvFileIfExists()
env.Dump(os.Getenv("PORT"))
// #string "9090"
```
_Example: default .env on a host_
```go
_ = os.WriteFile(".env", []byte("SERVICE=api\nAPP_DEBUG=3"), 0o644)
_ = env.LoadEnvFileIfExists()
env.Dump(os.Getenv("SERVICE"))
// #string "api"
```
## Runtime
Arch returns the CPU architecture the binary is running on.
_Example: print GOARCH_
```go
env.Dump(env.Arch())
// #string "amd64"
// #string "arm64"
```
IsBSD reports whether the runtime OS is any BSD variant.
```go
env.Dump(env.IsBSD())
// #bool true (on BSD variants)
// #bool false (elsewhere)
```
IsContainerOS reports whether this OS is *typically* used as a container base.
```go
env.Dump(env.IsContainerOS())
// #bool true (on Linux)
// #bool false (on macOS/Windows)
```
IsLinux reports whether the runtime OS is Linux.
```go
env.Dump(env.IsLinux())
// #bool true (on Linux)
// #bool false (on other OSes)
```
IsMac reports whether the runtime OS is macOS (Darwin).
```go
env.Dump(env.IsMac())
// #bool true (on macOS)
// #bool false (elsewhere)
```
IsUnix reports whether the OS is Unix-like.
```go
env.Dump(env.IsUnix())
// #bool true (on Unix-like OSes)
// #bool false (e.g., on Windows or Plan 9)
```
IsWindows reports whether the runtime OS is Windows.
```go
env.Dump(env.IsWindows())
// #bool true (on Windows)
// #bool false (elsewhere)
```
OS returns the current operating system identifier.
_Example: inspect GOOS_
```go
env.Dump(env.OS())
// #string "linux" (on Linux)
// #string "darwin" (on macOS)
// #string "windows" (on Windows)
```
## Typed getters
Get returns the environment variable for key or fallback when empty.
_Example: fallback when unset_
```go
os.Unsetenv("DB_HOST")
host := env.Get("DB_HOST", "localhost")
env.Dump(host)
// #string "localhost"
```
_Example: prefer existing value_
```go
_ = os.Setenv("DB_HOST", "db.internal")
host = env.Get("DB_HOST", "localhost")
env.Dump(host)
// #string "db.internal"
```
GetBool parses a boolean from an environment variable or fallback string.
_Example: numeric truthy_
```go
_ = os.Setenv("DEBUG", "1")
debug := env.GetBool("DEBUG", "false")
env.Dump(debug)
// #bool true
```
_Example: fallback string_
```go
os.Unsetenv("DEBUG")
debug = env.GetBool("DEBUG", "false")
env.Dump(debug)
// #bool false
```
GetDuration parses a Go duration string (e.g. "5s", "10m", "1h").
_Example: override request timeout_
```go
_ = os.Setenv("HTTP_TIMEOUT", "30s")
timeout := env.GetDuration("HTTP_TIMEOUT", "5s")
env.Dump(timeout)
// #time.Duration 30s
```
_Example: fallback when unset_
```go
os.Unsetenv("HTTP_TIMEOUT")
timeout = env.GetDuration("HTTP_TIMEOUT", "5s")
env.Dump(timeout)
// #time.Duration 5s
```
GetEnum ensures the environment variable's value is in the allowed list.
_Example: accept only staged environments_
```go
_ = os.Setenv("APP_ENV", "production")
appEnv := env.GetEnum("APP_ENV", "local", []string{"local", "staging", "production"})
env.Dump(appEnv)
// #string "production"
```
_Example: fallback when unset_
```go
os.Unsetenv("APP_ENV")
appEnv = env.GetEnum("APP_ENV", "local", []string{"local", "staging", "production"})
env.Dump(appEnv)
// #string "local"
```
GetFloat parses a float64 from an environment variable or fallback string.
_Example: override threshold_
```go
_ = os.Setenv("THRESHOLD", "0.82")
threshold := env.GetFloat("THRESHOLD", "0.75")
env.Dump(threshold)
// #float64 0.82
```
_Example: fallback with decimal string_
```go
os.Unsetenv("THRESHOLD")
threshold = env.GetFloat("THRESHOLD", "0.75")
env.Dump(threshold)
// #float64 0.75
```
GetInt parses an int from an environment variable or fallback string.
_Example: fallback used_
```go
os.Unsetenv("PORT")
port := env.GetInt("PORT", "3000")
env.Dump(port)
// #int 3000
```
_Example: env overrides fallback_
```go
_ = os.Setenv("PORT", "8080")
port = env.GetInt("PORT", "3000")
env.Dump(port)
// #int 8080
```
GetInt64 parses an int64 from an environment variable or fallback string.
_Example: parse large numbers safely_
```go
_ = os.Setenv("MAX_SIZE", "1048576")
size := env.GetInt64("MAX_SIZE", "512")
env.Dump(size)
// #int64 1048576
```
_Example: fallback when unset_
```go
os.Unsetenv("MAX_SIZE")
size = env.GetInt64("MAX_SIZE", "512")
env.Dump(size)
// #int64 512
```
GetMap parses key=value pairs separated by commas into a map.
_Example: parse throttling config_
```go
_ = 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_
```go
os.Unsetenv("LIMITS")
limits = env.GetMap("LIMITS", "")
env.Dump(limits)
// #map[string]string []
```
GetSlice splits a comma-separated string into a []string with trimming.
_Example: trimmed addresses_
```go
_ = 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_
```go
os.Unsetenv("PEERS")
peers = env.GetSlice("PEERS", "")
env.Dump(peers)
// #[]string []
```
GetUint parses a uint from an environment variable or fallback string.
_Example: defaults to fallback when missing_
```go
os.Unsetenv("WORKERS")
workers := env.GetUint("WORKERS", "4")
env.Dump(workers)
// #uint 4
```
_Example: uses provided unsigned value_
```go
_ = os.Setenv("WORKERS", "16")
workers = env.GetUint("WORKERS", "4")
env.Dump(workers)
// #uint 16
```
GetUint64 parses a uint64 from an environment variable or fallback string.
_Example: high range values_
```go
_ = os.Setenv("MAX_ITEMS", "5000")
maxItems := env.GetUint64("MAX_ITEMS", "100")
env.Dump(maxItems)
// #uint64 5000
```
_Example: fallback when unset_
```go
os.Unsetenv("MAX_ITEMS")
maxItems = env.GetUint64("MAX_ITEMS", "100")
env.Dump(maxItems)
// #uint64 100
```
MustGet returns the value of key or panics if missing/empty.
_Example: required secret_
```go
_ = os.Setenv("API_SECRET", "s3cr3t")
secret := env.MustGet("API_SECRET")
env.Dump(secret)
// #string "s3cr3t"
```
_Example: panic on missing value_
```go
os.Unsetenv("API_SECRET")
secret = env.MustGet("API_SECRET") // panics: env variable missing: API_SECRET
```
MustGetBool panics if missing or invalid.
_Example: gate features explicitly_
```go
_ = os.Setenv("FEATURE_ENABLED", "true")
enabled := env.MustGetBool("FEATURE_ENABLED")
env.Dump(enabled)
// #bool true
```
_Example: panic on invalid value_
```go
_ = os.Setenv("FEATURE_ENABLED", "maybe")
_ = env.MustGetBool("FEATURE_ENABLED") // panics when parsing
```
MustGetInt panics if the value is missing or not an int.
_Example: ensure numeric port_
```go
_ = os.Setenv("PORT", "8080")
port := env.MustGetInt("PORT")
env.Dump(port)
// #int 8080
```
_Example: panic on bad value_
```go
_ = os.Setenv("PORT", "not-a-number")
_ = env.MustGetInt("PORT") // panics when parsing
```
## 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.
## License
MIT