DevPrep
  • Interview Prep
  • Projects
  • Resources
  • Pricing
  • About Us
Submit Question
DevPrep
  • Pricing
  • About Us
Submit Question

Practice

  • JavaScript
  • DSA
  • Machine Coding
  • System Design

Resources

  • Learning Tracks
  • Articles
  • Roadmaps
  • Compare Concepts
  • Glossary
  • Developer Tools
  • All Questions

Company

  • About
  • Pricing

Legal

  • Privacy Policy
  • Terms of Service
DevPrep

© 2026 DevPrep. All rights reserved.

← Back to Questions
EasyTheory

Viper: Configuration Management in Go

49 views

Problem Statement

Implement 12-factor app configuration using Viper to read from files, environment variables, and flags with proper precedence.

Basic Viper Setup

package config

import (
    "github.com/spf13/viper"
)

type Config struct {
    Server   ServerConfig
    Database DatabaseConfig
    Redis    RedisConfig
}

type ServerConfig struct {
    Port    int    `mapstructure:"port"`
    Host    string `mapstructure:"host"`
    Debug   bool   `mapstructure:"debug"`
}

type DatabaseConfig struct {
    Host     string `mapstructure:"host"`
    Port     int    `mapstructure:"port"`
    Name     string `mapstructure:"name"`
    User     string `mapstructure:"user"`
    Password string `mapstructure:"password"`
}

func Load() (*Config, error) {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath(".")
    viper.AddConfigPath("./config")
    
    // Environment variable support
    viper.AutomaticEnv()
    viper.SetEnvPrefix("APP")
    
    // Set defaults
    viper.SetDefault("server.port", 8080)
    viper.SetDefault("server.host", "0.0.0.0")
    
    if err := viper.ReadInConfig(); err != nil {
        // Config file not found is OK
        if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
            return nil, err
        }
    }
    
    var cfg Config
    if err := viper.Unmarshal(&cfg); err != nil {
        return nil, err
    }
    
    return &cfg, nil
}

Config File (config.yaml)

server:
  port: 8080
  host: "0.0.0.0"
  debug: false

database:
  host: "localhost"
  port: 5432
  name: "myapp"
  user: "postgres"
  password: "${DB_PASSWORD}"  # Reference env var

redis:
  host: "localhost"
  port: 6379

Environment Variable Override

# Override with environment variables
export APP_SERVER_PORT=9090
export APP_DATABASE_HOST=db.prod.example.com

./myapp
# Uses port 9090 and production database

Config File Watching

func WatchConfig(onChange func()) {
    viper.WatchConfig()
    viper.OnConfigChange(func(e fsnotify.Event) {
        log.Println("Config file changed:", e.Name)
        onChange()
    })
}

// Usage:
WatchConfig(func() {
    newCfg, _ := Load()
    updateRuntimeConfig(newCfg)
})

Command Line Flags with pflag

import (
    "github.com/spf13/pflag"
    "github.com/spf13/viper"
)

func init() {
    pflag.Int("port", 8080, "Server port")
    pflag.Bool("debug", false, "Enable debug mode")
    pflag.Parse()
    
    viper.BindPFlags(pflag.CommandLine)
}

// Precedence: flags > env > config file > defaults

Best Practices

  • Always unmarshal into a struct (type safety)
  • Use SetDefault for all config values
  • Don't read viper.GetString() everywhere - load once at startup
  • Validate config immediately after loading

Sample Test Cases

Case 1
Input
viper.SetConfigFile("config.yaml")
Expected Output
Reads configuration from YAML file
Case 2
Input
viper.AutomaticEnv()
Expected Output
Environment variables override config

No solutions yet

Be the first to share a solution for this question.

Comments (0)

Sign in to leave a comment.

No comments yet. Be the first to comment.

Stats

Views
49
Likes
0
Solutions
0
Comments
0

Category

Backend Engineering

Languages

Go