Skip to main content
โšก Calmops

Configuration Management in Go

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

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