Use Viper to manage configuration by setting up a config file (like YAML or JSON), enabling environment variable overrides, and binding flags to your config keys. Initialize Viper, set the config file name and search paths, read the config, and then unmarshal it into your application's struct or access values directly using Get methods.
Here is a practical example using a config.yaml file and environment variables:
config.yaml
server:
port: 8080
host: localhost
debug: false
main.go
package main
import (
"fmt"
"log"
"os"
"github.com/spf13/viper"
)
type Config struct {
Server struct {
Port int `mapstructure:"port"`
Host string `mapstructure:"host"`
} `mapstructure:"server"`
Debug bool `mapstructure:"debug"`
}
func main() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AddConfigPath("/etc/myapp/")
viper.AddConfigPath("$HOME/.myapp")
// Allow environment variables to override config
viper.AutomaticEnv()
// Map environment variable "APP_DEBUG" to "debug" key
viper.SetEnvPrefix("APP")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
log.Println("Config file not found, using defaults/env vars")
} else {
log.Fatalf("Error reading config file: %v", err)
}
}
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
log.Fatalf("Error unmarshaling config: %v", err)
}
// Override via env var: export APP_SERVER_PORT=9090
port := viper.GetInt("server.port")
fmt.Printf("Server running on %s:%d (Debug: %v)\n", cfg.Server.Host, port, cfg.Debug)
}
Run the application with an environment variable override:
export APP_SERVER_PORT=9090
export APP_DEBUG=true
go run main.go
Viper automatically merges configuration sources in this order: defaults, config file, environment variables, and command-line flags. The mapstructure tags in your struct ensure that nested keys like server.port map correctly to your Go fields. If you need to support command-line flags, use viper.BindPFlag("key", flag) after defining your flags with the flag package or cobra. Always check for errors when reading the config file, but allow the application to start if the file is missing, relying on defaults or environment variables instead.