Go (Golang) Application Configuration: Libraries and Tools
Configuration is one of the key aspects of developing modern applications in Go. Flexibility, security, and ease of settings management directly impact the stability and scalability of a service. The Golang ecosystem has several mature libraries that handle loading configurations from files (JSON, YAML, TOML), environment variables, command-line flags, and remote sources. In this article, we will look at four of the most popular tools: viper, envconfig, koanf, and cleanenv, as well as basic approaches to managing settings.
Why do you need a separate library for configuration?
The Go standard library provides the flag package for parsing command-line arguments and os.Getenv for reading environment variables. However, real-world projects require:
- Support for multiple file formats (JSON, YAML, TOML, HCL).
- Reading from multiple sources with priorities (file → environment variables → flags).
- Automatic mapping of struct fields to configuration keys.
- Validation of required fields and types.
- Hot-reload (reloading configuration without restarting the application).
The libraries described below solve all these tasks.
1. Viper — The Universal King of Configuration
Viper is the most popular configuration library in Go (over 25k stars on GitHub). It supports all major formats, environment variables, remote systems (etcd, Consul), and command-line flags.
Installation
go get github.com/spf13/viperUsage Example (YAML + environment variables)
package main
import ( "fmt" "log" "github.com/spf13/viper")
type Config struct { Port int `mapstructure:"port"` DBHost string `mapstructure:"db_host"` Debug bool `mapstructure:"debug"`}
func main() { viper.SetConfigName("config") // file name without extension viper.SetConfigType("yaml") // format viper.AddConfigPath(".") // search path viper.AutomaticEnv() // automatically read environment variables
if err := viper.ReadInConfig(); err != nil { log.Fatalf("Error reading config: %v", err) }
var cfg Config if err := viper.Unmarshal(&cfg); err != nil { log.Fatalf("Error unmarshaling: %v", err) }
fmt.Printf("Port: %d, DB Host: %s, Debug: %t", cfg.Port, cfg.DBHost, cfg.Debug)}Features:
- Support for nested keys via
viper.GetString("app.database.host"). - Priority: explicit call to Set → flag → env → config file → default.
- Live reload capability via
viper.WatchConfig().
2. envconfig — Minimalism for 12-Factor Apps
If your application follows the 12-factor app methodology and uses only environment variables, envconfig is the ideal choice. It automatically populates a struct based on envconfig tags.
Installation
go get github.com/kelseyhightower/envconfigExample
package main
import ( "fmt" "github.com/kelseyhightower/envconfig")
type Config struct { Port int `envconfig:"PORT" default:"8080"` DBHost string `envconfig:"DB_HOST" required:"true"` Debug bool `envconfig:"DEBUG"`}
func main() { var cfg Config if err := envconfig.Process("myapp", &cfg); err != nil { panic(err) } fmt.Printf("Config: %+v", cfg)}Advantages:
- Minimal code — just declare a struct