gonfiguration 🔧: The Ultimate Golang Config Chaos

If you’re tired of writing the same env var parsing bullshit over and over, gonfiguration handles all the annoying shit for you. It’s got reflection-based mapping, supports all the types you actually use, and doesn’t crash when someone fucks up the config.

Here’s How It Works

package main
import (
	"fmt"
	"log"
	"os"
	"time"
	"github.com/psyb0t/gonfiguration"
)
type AppConfig struct {
	// Basic configuration
	AppName     string `env:"APP_NAME"`
	Debug       bool   `env:"DEBUG"`
	Port        int    `env:"PORT"`
	// String slices for lists
	AllowedHosts []string `env:"ALLOWED_HOSTS"`
	Features     []string `env:"FEATURES"`
	// Time duration fields
	Timeout      time.Duration `env:"TIMEOUT"`
	RetryDelay   time.Duration `env:"RETRY_DELAY"`
	// Numeric types
	MaxUsers     int64   `env:"MAX_USERS"`
	CacheSize    uint32  `env:"CACHE_SIZE"`
	LoadFactor   float64 `env:"LOAD_FACTOR"`
	// Database configuration
	DBDSN        string `env:"DB_DSN"`
	DBName       string `env:"DB_NAME"`
	DBUser       string `env:"DB_USER"`
	DBPass       string `env:"DB_PASS"`
}
func main() {
	cfg := AppConfig{}
	// Set sensible defaults
	gonfiguration.SetDefaults(map[string]interface{}{
		"APP_NAME":      "MyAwesomeApp",
		"DEBUG":         false,
		"PORT":          8080,
		"ALLOWED_HOSTS": []string{"localhost", "127.0.0.1", "*.example.com"},
		"FEATURES":      []string{"auth", "logging", "metrics"},
		"TIMEOUT":       30 * time.Second,
		"RETRY_DELAY":   5 * time.Second,
		"MAX_USERS":     1000,
		"CACHE_SIZE":    256,
		"LOAD_FACTOR":   0.75,
		"DB_DSN":        "postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable",
	})
	// Set some env vars (normally these come from your environment)
	os.Setenv("APP_NAME", "ProductionBeast")
	os.Setenv("DEBUG", "true")
	os.Setenv("ALLOWED_HOSTS", "api.myapp.com, cdn.myapp.com , admin.myapp.com")
	os.Setenv("FEATURES", "auth,logging,metrics,analytics,caching")
	os.Setenv("TIMEOUT", "1m30s")
	os.Setenv("MAX_USERS", "50000")
	os.Setenv("DB_NAME", "production_db")
	os.Setenv("DB_USER", "app_user")
	os.Setenv("DB_PASS", "super-secret-password")
	// Parse it
	if err := gonfiguration.Parse(&cfg); err != nil {
		log.Fatalf("Config parsing fucked up: %v", err)
	}
	// Check what we got
	fmt.Printf("🚀 App: %s (Debug: %v)\n", cfg.AppName, cfg.Debug)
	fmt.Printf("🌐 Listening on port: %d\n", cfg.Port)
	fmt.Printf("🏠 Allowed hosts: %v\n", cfg.AllowedHosts)
	fmt.Printf("⭐ Features enabled: %v\n", cfg.Features)
	fmt.Printf("⏱️ Timeout: %v, Retry delay: %v\n", cfg.Timeout, cfg.RetryDelay)
	fmt.Printf("👥 Max users: %d, Cache size: %d MB\n", cfg.MaxUsers, cfg.CacheSize)
	fmt.Printf("📊 Load factor: %.2f\n", cfg.LoadFactor)
	fmt.Printf("🗄️ Database: %s@%s\n", cfg.DBUser, cfg.DBName)
}

Output:

🚀 App: ProductionBeast (Debug: true)
🌐 Listening on port: 8080
🏠 Allowed hosts: [api.myapp.com cdn.myapp.com admin.myapp.com]
⭐ Features enabled: [auth logging metrics analytics caching]
⏱️ Timeout: 1m30s, Retry delay: 5s
👥 Max users: 50000, Cache size: 256 MB
📊 Load factor: 0.75
🗄️ Database: app_user@production_db

Installation

go get github.com/psyb0t/gonfiguration

That’s it. Minimal deps, no bullshit setup.

How It Works

1. Define Your Config Struct

Tag your struct fields with env:"VAR_NAME" and use whatever types you need:

type UltimateConfig struct {
	// Basic types
	AppName  string `env:"APP_NAME"`
	Debug    bool   `env:"DEBUG"`
	// Integer types
	Count    int    `env:"COUNT"`
	SmallNum int8   `env:"SMALL_NUM"`
	BigNum   int64  `env:"BIG_NUM"`
	// Unsigned integers
	UserID   uint32 `env:"USER_ID"`
	FileSize uint64 `env:"FILE_SIZE"`
	// Floating point numbers
	Ratio     float32 `env:"RATIO"`
	Precision float64 `env:"PRECISION"`
	// Time durations
	Timeout   time.Duration `env:"TIMEOUT"`
	Interval  time.Duration `env:"INTERVAL"`
	// String slices
	Tags      []string `env:"TAGS"`
	Servers   []string `env:"SERVERS"`
}

2. Set Defaults

Don’t let your app crash because someone forgot an env var:

gonfiguration.SetDefaults(map[string]interface{}{
	"APP_NAME":   "MyApp",
	"DEBUG":      false,
	"COUNT":      100,
	"RATIO":      0.8,
	"TIMEOUT":    30*time.Second,
	"TAGS":       []string{"prod", "api", "web"},
	"SERVERS":    []string{"srv1.com", "srv2.com", "srv3.com"},
})

3. Parse It

cfg := UltimateConfig{}
if err := gonfiguration.Parse(&cfg); err != nil {
	log.Fatalf("Config parsing shit the bed: %v", err)
}

4. Done

Your config is loaded. Use it however you want.

String Slices Actually Work

Set a comma-separated env var and get a proper []string:

type Config struct {
	Microservices []string `env:"MICROSERVICES"`
	APIKeys       []string `env:"API_KEYS"`
}
os.Setenv("MICROSERVICES", "auth-service, user-service , payment-service,  notification-service")
os.Setenv("API_KEYS", "key1,key2,key3")
cfg := Config{}
gonfiguration.Parse(&cfg)
// You get:
// cfg.Microservices = ["auth-service", "user-service", "payment-service", "notification-service"]
// cfg.APIKeys = ["key1", "key2", "key3"]

Empty strings become empty slices:

os.Setenv("OPTIONAL_FEATURES", "")
// Results in: cfg.OptionalFeatures = []string{}

Time Durations Don’t Suck

Use Go’s duration strings and get actual time.Duration values:

type TimingConfig struct {
	HTTPTimeout    time.Duration `env:"HTTP_TIMEOUT"`
	RetryInterval  time.Duration `env:"RETRY_INTERVAL"`
	CacheExpiry    time.Duration `env:"CACHE_EXPIRY"`
}
os.Setenv("HTTP_TIMEOUT", "30s")
os.Setenv("RETRY_INTERVAL", "5m30s")
os.Setenv("CACHE_EXPIRY", "24h")
cfg := TimingConfig{}
gonfiguration.Parse(&cfg)
// You get actual durations:
// cfg.HTTPTimeout = 30 * time.Second
// cfg.RetryInterval = 5*time.Minute + 30*time.Second
// cfg.CacheExpiry = 24 * time.Hour

Debugging Your Config

Get all the values if you need to debug something:

allValues := gonfiguration.GetAllValues()
fmt.Printf("All config values: %+v\n", allValues)
defaults := gonfiguration.GetDefaults()
fmt.Printf("Default values: %+v\n", defaults)
envVars := gonfiguration.GetEnvVars()
fmt.Printf("Environment variables: %+v\n", envVars)
// Reset everything if needed
gonfiguration.Reset()

It’s thread-safe, so concurrent access won’t fuck things up:

// Won't break
go func() {
	gonfiguration.SetDefault("KEY1", "value1")
}()
go func() {
	cfg := MyConfig{}
	gonfiguration.Parse(&cfg)
}()

When Things Go Wrong

You get actual error messages instead of cryptic bullshit:

// Forgot the pointer?
err := gonfiguration.Parse(cfg) // Should be &cfg
// Error: "yo, the destination ain't a pointer"
// Wrong type in env var?
os.Setenv("PORT", "not-a-number")
err := gonfiguration.Parse(&cfg)
// Error: "wtf.. Failed to parse int: strconv.ParseInt: parsing \"not-a-number\": invalid syntax"

Real Example

Web service config that doesn’t suck:

type WebServiceConfig struct {
	// Server configuration
	ListenAddress string        `env:"LISTEN_ADDRESS"`
	Port          int           `env:"PORT"`
	ReadTimeout   time.Duration `env:"READ_TIMEOUT"`
	WriteTimeout  time.Duration `env:"WRITE_TIMEOUT"`
	// Security
	AllowedOrigins []string `env:"ALLOWED_ORIGINS"`
	APIKeys        []string `env:"API_KEYS"`
	RateLimitRPS   int      `env:"RATE_LIMIT_RPS"`
	// Database
	DBHost     string `env:"DB_HOST"`
	DBPort     int    `env:"DB_PORT"`
	DBUser     string `env:"DB_USER"`
	DBPassword string `env:"DB_PASSWORD"`
	DBName     string `env:"DB_NAME"`
	// Redis cache
	RedisURL     string        `env:"REDIS_URL"`
	CacheExpiry  time.Duration `env:"CACHE_EXPIRY"`
	// Logging
	LogLevel     string   `env:"LOG_LEVEL"`
	LogOutputs   []string `env:"LOG_OUTPUTS"`
	// Feature flags
	EnableMetrics bool `env:"ENABLE_METRICS"`
	EnableTracing bool `env:"ENABLE_TRACING"`
}
func main() {
	cfg := WebServiceConfig{}
	gonfiguration.SetDefaults(map[string]interface{}{
		"LISTEN_ADDRESS":  "0.0.0.0",
		"PORT":           8080,
		"READ_TIMEOUT":   30*time.Second,
		"WRITE_TIMEOUT":  30*time.Second,
		"ALLOWED_ORIGINS": []string{"https://myapp.com", "https://admin.myapp.com"},
		"RATE_LIMIT_RPS": 100,
		"DB_HOST":        "localhost",
		"DB_PORT":        5432,
		"CACHE_EXPIRY":   1*time.Hour,
		"LOG_LEVEL":      "info",
		"LOG_OUTPUTS":    []string{"stdout", "file"},
		"ENABLE_METRICS": true,
		"ENABLE_TRACING": false,
	})
	if err := gonfiguration.Parse(&cfg); err != nil {
		log.Fatalf("Config fucked up: %v", err)
	}
	// Go build your app
	startWebService(cfg)
}

That’s It

If you’re still manually parsing environment variables in 2025, use this instead.

GitHubgithub.com/psyb0t/gonfiguration

Post updated: 11 Sept 2025