Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mnah05-boiler-go-21-79.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Boiler-Go uses environment variables for configuration with strong validation and fail-fast error handling. Configuration is managed in internal/config/config.go.

Configuration structure

All configuration is defined in the Config struct:
type Config struct {
    // Server
    AppPort string `env:"APP_PORT" envDefault:"8080"`
    
    // Database
    DatabaseURL string `env:"DATABASE_URL,required"`
    
    // Redis / Asynq
    RedisAddr     string `env:"REDIS_ADDR,required"`
    RedisPassword string `env:"REDIS_PASSWORD"`
    RedisDB       int    `env:"REDIS_DB" envDefault:"0"`
    
    // Timeouts
    HealthCheckTimeout    time.Duration `env:"HEALTH_CHECK_TIMEOUT" envDefault:"2s"`
    APIShutdownTimeout    time.Duration `env:"API_SHUTDOWN_TIMEOUT" envDefault:"10s"`
    WorkerShutdownTimeout time.Duration `env:"WORKER_SHUTDOWN_TIMEOUT" envDefault:"30s"`
    
    // Logging
    LogOutput string `env:"LOG_OUTPUT" envDefault:"stdout"`
    LogFile   string `env:"LOG_FILE"`
}
Fields tagged with ,required must be provided. The application will fail to start if they’re missing.

Loading configuration

Configuration is loaded once using the singleton pattern:
var (
    cfg  *Config
    once sync.Once
)

func Load(logg zerolog.Logger) *Config {
    once.Do(func() {
        // Load .env file if present
        if err := godotenv.Load(); err != nil {
            logg.Info().Msg("no .env file found (using system environment)")
        }
        
        // Parse environment variables
        c := Config{}
        if err := env.Parse(&c); err != nil {
            logg.Fatal().Err(err).Msg("failed to load config")
        }
        
        // Validate configuration
        // ... validation logic ...
        
        cfg = &c
    })
    
    return cfg
}
1

Load .env file

Attempts to load .env file from the current directory (optional)
2

Parse environment

Parses environment variables into the Config struct using struct tags
3

Validate values

Runs comprehensive validation on all configuration values
4

Store singleton

Stores the validated config for the application lifetime

Environment variables

Server configuration

APP_PORT
string
default:"8080"
HTTP server port. Must be between 1 and 65535.
APP_PORT=8080

Database configuration

DATABASE_URL
string
required
PostgreSQL connection string. Must use postgres:// or postgresql:// scheme.
DATABASE_URL=postgres://user:pass@localhost:5432/dbname
The URL is validated in internal/config/config.go:115-132:
func validateDatabaseURL(dbURL string) error {
    u, err := url.Parse(dbURL)
    if err != nil {
        return fmt.Errorf("invalid URL format: %w", err)
    }
    
    // Check scheme
    if u.Scheme != "postgres" && u.Scheme != "postgresql" {
        return fmt.Errorf("URL scheme must be 'postgres' or 'postgresql', got '%s'", u.Scheme)
    }
    
    // Check host
    if u.Host == "" {
        return fmt.Errorf("URL must contain a host")
    }
    
    // Check database name
    if u.Path == "" || u.Path == "/" {
        return fmt.Errorf("URL must contain a database name")
    }
    
    return nil
}

Redis configuration

REDIS_ADDR
string
required
Redis server address in host:port format.
REDIS_ADDR=localhost:6379
REDIS_PASSWORD
string
Redis password. Optional for development, required for production.
REDIS_PASSWORD=your-secure-password
REDIS_DB
int
default:"0"
Redis database index (0-15). Validated to be within valid range.
REDIS_DB=0
Redis database index must be between 0 and 15. Values outside this range will cause startup failure:
// internal/config/config.go:72-74
if c.RedisDB < 0 || c.RedisDB > 15 {
    logg.Fatal().Msg("REDIS_DB must be between 0 and 15")
}

Timeout configuration

HEALTH_CHECK_TIMEOUT
duration
default:"2s"
Maximum time for health check requests to complete.
HEALTH_CHECK_TIMEOUT=2s
API_SHUTDOWN_TIMEOUT
duration
default:"10s"
Maximum time to wait for API server graceful shutdown.
API_SHUTDOWN_TIMEOUT=10s
WORKER_SHUTDOWN_TIMEOUT
duration
default:"30s"
Maximum time to wait for worker tasks to complete during shutdown.
WORKER_SHUTDOWN_TIMEOUT=30s
Set this higher than your longest-running task duration to avoid force-killing tasks.

Logging configuration

LOG_OUTPUT
string
default:"stdout"
Logging destination: stdout, file, or both.
LOG_OUTPUT=stdout
LOG_FILE
string
Log file path when using file or both mode.
LOG_FILE=logs/app.log
If not specified, defaults to:
  • API: logs/api.log
  • Worker: logs/worker.log

Validation

Configuration is validated comprehensively during startup. All validation happens in config.Load():
// internal/config/config.go:61-63
if c.DatabaseURL == "" {
    logg.Fatal().Msg("DATABASE_URL is required")
}

Fail-fast philosophy

Boiler-Go validates all configuration at startup and exits immediately if anything is wrong:
FATA failed to load config error="REDIS_DB must be between 0 and 15"
Application exits immediately with a clear error message.
This fail-fast approach ensures you catch configuration errors during deployment, not in production.

Example configurations

Development

.env
# Server
APP_PORT=8080

# Database
DATABASE_URL=postgres://user:pass@localhost:5432/boiler_dev

# Redis
REDIS_ADDR=localhost:6379
REDIS_PASSWORD=
REDIS_DB=0

# Timeouts
HEALTH_CHECK_TIMEOUT=2s
API_SHUTDOWN_TIMEOUT=10s
WORKER_SHUTDOWN_TIMEOUT=30s

# Logging
LOG_OUTPUT=stdout

Production

.env.production
# Server
APP_PORT=8080

# Database
DATABASE_URL=postgres://user:secure-pass@db.example.com:5432/boiler_prod

# Redis
REDIS_ADDR=redis.example.com:6379
REDIS_PASSWORD=secure-redis-password
REDIS_DB=0

# Timeouts
HEALTH_CHECK_TIMEOUT=5s
API_SHUTDOWN_TIMEOUT=30s
WORKER_SHUTDOWN_TIMEOUT=60s

# Logging
LOG_OUTPUT=both
LOG_FILE=/var/log/boiler-go/app.log
Never commit .env files containing production credentials to version control.

Usage in code

Load at startup

// cmd/api/main.go:53
cfg := config.Load(logger.New())

Access configuration values

func setupServer(cfg *config.Config) {
    server := &http.Server{
        Addr:           ":" + cfg.AppPort,
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
    }
}

Best practices

  1. Load once: Configuration is loaded once at startup using sync.Once
  2. Validate early: All validation happens during Load(), before any services start
  3. Use .env for local dev: Keep .env files for local development, use system environment in production
  4. Document defaults: All default values are visible in struct tags
  5. Type safety: Use appropriate types (time.Duration, int) instead of strings
Use different .env files for different environments:
# Development
cp .env.example .env

# Production
source .env.production
./boiler-go