Configuration Management in Go
Introduction
Proper configuration management is essential for deploying applications across environments. This guide covers environment-based configuration, secrets management, and best practices.
Core Concepts
Configuration Types
- Environment Variables: Runtime configuration
- Config Files: Structured configuration
- Secrets: Sensitive credentials
- Feature Flags: Runtime behavior control
Good: Environment-Based Configuration
Configuration Struct
package main
import (
"os"
"strconv"
)
// โ
GOOD: Configuration struct
type Config struct {
Port int
DatabaseURL string
LogLevel string
Environment string
JWTSecret string
APIKey string
}
// โ
GOOD: Load from environment
func LoadConfig() *Config {
return &Config{
Port: getEnvInt("PORT", 8080),
DatabaseURL: getEnv("DATABASE_URL", ""),
LogLevel: getEnv("LOG_LEVEL", "info"),
Environment: getEnv("ENVIRONMENT", "development"),
JWTSecret: getEnv("JWT_SECRET", ""),
APIKey: getEnv("API_KEY", ""),
}
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func getEnvInt(key string, defaultValue int) int {
if value := os.Getenv(key); value != "" {
if intVal, err := strconv.Atoi(value); err == nil {
return intVal
}
}
return defaultValue
}
// โ
GOOD: Validate configuration
func (c *Config) Validate() error {
if c.DatabaseURL == "" {
return fmt.Errorf("DATABASE_URL is required")
}
if c.JWTSecret == "" {
return fmt.Errorf("JWT_SECRET is required")
}
return nil
}
Good: Secrets Management
Using Environment Variables
// โ
GOOD: Load secrets from environment
func LoadSecrets() *Secrets {
return &Secrets{
DatabasePassword: os.Getenv("DB_PASSWORD"),
APIKey: os.Getenv("API_KEY"),
JWTSecret: os.Getenv("JWT_SECRET"),
}
}
// โ BAD: Hardcoded secrets
const (
DatabasePassword = "password123"
APIKey = "sk_live_abc123"
)
Using .env Files
// โ
GOOD: Load from .env file
import "github.com/joho/godotenv"
func init() {
godotenv.Load()
}
// .env file
// DATABASE_URL=postgres://user:password@localhost/db
// JWT_SECRET=secret-key
// API_KEY=api-key
Good: Configuration Files
YAML Configuration
package main
import (
"gopkg.in/yaml.v2"
"io/ioutil"
)
// โ
GOOD: YAML configuration
type AppConfig struct {
Server struct {
Port int `yaml:"port"`
Host string `yaml:"host"`
} `yaml:"server"`
Database struct {
URL string `yaml:"url"`
} `yaml:"database"`
}
func LoadConfigFromFile(filename string) (*AppConfig, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var config AppConfig
if err := yaml.Unmarshal(data, &config); err != nil {
return nil, err
}
return &config, nil
}
Advanced Patterns
Feature Flags
// โ
GOOD: Feature flags
type FeatureFlags struct {
NewUI bool
BetaFeature bool
Maintenance bool
}
func LoadFeatureFlags() *FeatureFlags {
return &FeatureFlags{
NewUI: getEnvBool("FEATURE_NEW_UI", false),
BetaFeature: getEnvBool("FEATURE_BETA", false),
Maintenance: getEnvBool("MAINTENANCE_MODE", false),
}
}
func getEnvBool(key string, defaultValue bool) bool {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value == "true" || value == "1"
}
Configuration Validation
// โ
GOOD: Validate configuration
func (c *Config) Validate() error {
if c.Port < 1 || c.Port > 65535 {
return fmt.Errorf("invalid port: %d", c.Port)
}
if c.Environment != "dev" && c.Environment != "prod" {
return fmt.Errorf("invalid environment: %s", c.Environment)
}
if c.DatabaseURL == "" {
return fmt.Errorf("DATABASE_URL is required")
}
return nil
}
Best Practices
1. Use Environment Variables
// โ
GOOD: Environment variables
port := os.Getenv("PORT")
// โ BAD: Hardcoded values
port := "8080"
2. Validate Configuration
// โ
GOOD: Validate on startup
config := LoadConfig()
if err := config.Validate(); err != nil {
log.Fatal(err)
}
// โ BAD: No validation
config := LoadConfig()
3. Use Secrets Management
// โ
GOOD: Use secrets manager
secret := os.Getenv("API_KEY")
// โ BAD: Hardcoded secrets
secret := "sk_live_abc123"
Resources
- 12 Factor App: https://12factor.net/config
- godotenv: https://github.com/joho/godotenv
- Viper: https://github.com/spf13/viper
Summary
Proper configuration management enables flexible deployment across environments. Use environment variables for configuration, validate on startup, and never hardcode secrets. Following these practices ensures secure, maintainable applications.
Comments