Go cookbook · Beginner
Load and validate configuration from environment variables
Configuration is read ad hoc with os.Getenv all over the code, and typos are discovered in production.
Approach
Load configuration once at startup into a struct, parse types explicitly, collect all problems and fail with a clear message, then pass the struct to the components that need it.
Solution
package config
import (
"errors"
"fmt"
"os"
"strconv"
"time"
)
type Config struct {
Addr string
DatabaseURL string
ShutdownTimeout time.Duration
MaxUploadMB int
Debug bool
}
func Load() (Config, error) {
var errs []error
get := func(key, def string) string {
if v, ok := os.LookupEnv(key); ok {
return v
}
return def
}
required := func(key string) string {
v, ok := os.LookupEnv(key)
if !ok || v == "" {
errs = append(errs, fmt.Errorf("%s is required", key))
}
return v
}
cfg := Config{Addr: get("ADDR", ":8080"), DatabaseURL: required("DATABASE_URL")}
var err error
if cfg.ShutdownTimeout, err = time.ParseDuration(get("SHUTDOWN_TIMEOUT", "25s")); err != nil {
errs = append(errs, fmt.Errorf("SHUTDOWN_TIMEOUT: %w", err))
}
if cfg.MaxUploadMB, err = strconv.Atoi(get("MAX_UPLOAD_MB", "10")); err != nil || cfg.MaxUploadMB < 1 {
errs = append(errs, errors.New("MAX_UPLOAD_MB must be a positive integer"))
}
if cfg.Debug, err = strconv.ParseBool(get("DEBUG", "false")); err != nil {
errs = append(errs, fmt.Errorf("DEBUG: %w", err))
}
return cfg, errors.Join(errs...)
}
Pitfalls
- Reading environment variables deep inside packages, making them hard to test.
- Silent defaults for required values like database URLs.
- Printing the config at startup including secrets.
Variations
github.com/caarlos0/envorkelseyhightower/envconfigmap variables to struct tags.- Combine with
flagfor command-line overrides in CLIs.
Further reading: os.LookupEnv
Reference: https://pkg.go.dev/os#LookupEnv
More Go recipes
- Benchmark code with testing.B.Loop
- Build a small, secure container image for a Go service
- Decode and validate JSON request bodies safely
- Embed static files and templates in the binary
- Log structured JSON with slog and redact secrets
- Process jobs concurrently with a bounded worker pool
- Profile a running service with pprof
- Rate limit requests per client