Master Go Configuration with Viper: A Step-by-Step Guide
This guide walks through installing Viper, creating common and environment‑specific YAML configuration files, mapping them to Go structs, setting up a global configuration holder, and initializing the config with live watching, providing a complete, runnable example for Go backend projects.
1 Introduction
Viper (https://github.com/spf13/viper) is a complete configuration solution for Go applications, designed to work inside applications and handle all types of configuration needs and formats. It currently has 26.6k stars and supports features such as:
Setting default values
Reading configuration from JSON, TOML, YAML, HCL, envfile and Java properties files
Live watching and re-reading configuration files (optional)
Reading from environment variables
Reading from remote configuration systems (etcd or Consul) and monitoring changes
Reading from command‑line arguments
Reading from a buffer
Explicitly setting configuration values
2 Using Viper in a Golang Project
2.1 Installing Viper
<code># In terminal
go get github.com/spf13/viper
</code>2.2 Writing a common configuration file
Because Viper supports multiple file formats, we use a YAML example. Create a conf folder with a files sub‑folder and add a config.yaml file containing basic configuration:
<code>app:
env: local
port: 8888
app_name: traffic-demo
app_url: http://localhost
MySQL:
host: 127.0.0.1
port: 3306
user: root
password: <PASSWORD>
db_name: traffic
</code>This defines two configuration sections: app and MySQL .
2.3 Writing environment‑specific custom configuration files
Place four folders ( local , dev , beta , prod ) under config/files/ , each containing a custom.yaml . The file loaded depends on the value of app.env . Example local/custom.yaml :
<code>white_list:
user_id: # User ID list
- 063105015
- 063105024
- 063105028
request_path: # Access paths
- /api/v1/users
- /api/v1/ops
</code>2.4 Mapping configuration to structs
Create a conf/model folder with config.go and custom.go to define Go structs that map the configuration.
2.4.1 config.go
<code>package config
// Configuration aggregates the config sections
type Configuration struct {
App App `mapstructure:"app" json:"app" yaml:"app"`
MYSQL MYSQL `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
}
// App configuration
type App struct {
Env string `mapstructure:"env" json:"env" yaml:"env"`
Port string `mapstructure:"port" json:"port" yaml:"port"`
AppName string `mapstructure:"app_name" json:"app_name" yaml:"app_name"`
AppUrl string `mapstructure:"app_url" json:"app_url" yaml:"app_url"`
}
// MySQL configuration
type MYSQL struct {
Host string `mapstructure:"host" json:"host" yaml:"host"`
Port string `mapstructure:"port" json:"port" yaml:"port"`
User string `mapstructure:"user" json:"user" yaml:"user"`
Password string `mapstructure:"password" json:"password" yaml:"password"`
DbName string `mapstructure:"db_name" json:"db_name" yaml:"db_name"`
}
</code>2.4.2 custom.go
<code>package config
type Custom struct {
WhiteList WhiteList `mapstructure:"white_list" json:"white_list" yaml:"white_list"`
}
type WhiteList struct {
UserId []string `mapstructure:"user_id" json:"user_id" yaml:"user_id"`
RequestPath []string `mapstructure:"request_path" json:"request_path" yaml:"request_path"`
}
</code>2.5 Creating a global variable for configuration
Define a global/app.go file with an Application struct that holds the Viper instance, the parsed Configuration , and the custom configuration.
<code>package global
import (
"github.com/spf13/viper"
config "traffic.demo/config/model"
)
type Application struct {
ConfigViper *viper.Viper
Config config.Configuration
Custom config.Custom
}
// Global instance
var App = new(Application)
</code>2.5 Key step: struct mapping logic
The core parsing logic resides in bootstrap/config.go :
<code>package bootstrap
import (
"fmt"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"traffic.demo/global"
)
// Generic function to assemble configuration and return a *viper.Viper
func configAssemble[T any](configPath string, viperStruct T) *viper.Viper {
v := viper.New()
v.SetConfigFile(configPath)
v.SetConfigType("yaml")
if err := v.ReadInConfig(); err != nil {
panic(fmt.Errorf("read config failed: %s \n", err))
}
v.WatchConfig()
v.OnConfigChange(func(in fsnotify.Event) {
fmt.Println("config file changed:", in.Name)
if err := v.Unmarshal(viperStruct); err != nil {
fmt.Println(err)
}
})
if err := v.Unmarshal(viperStruct); err != nil {
fmt.Println(err)
}
return v
}
// InitializeConfig loads the main and environment‑specific configs
func InitializeConfig() {
config := "conf/files/config.yaml"
configAssemble(config, &global.App.Config)
customConfig := fmt.Sprintf("%s%s%s", "conf/files/", global.App.Config.App.Env, "/custom.yaml")
configAssemble(customConfig, &global.App.Custom)
}
</code>2.6 Overall file structure
2.7 Running the program
main.go loads the configuration and prints it:
<code>package main
import (
"fmt"
"traffic.demo/global"
)
func main() {
bootstrap.InitializeConfig()
fmt.Println("Traffic Service Started...!")
fmt.Printf("globalCong: %+v\n", global.App.Config)
fmt.Printf("customCong: %+v\n", global.App.Custom)
}
</code>Execution output shows the loaded configuration (screenshot omitted).
3 Summary
Viper is a powerful, simple, and easy‑to‑use Go configuration library that helps developers manage application settings flexibly.
Architecture & Thinking
🍭 Frontline tech director and chief architect at top-tier companies 🥝 Years of deep experience in internet, e‑commerce, social, and finance sectors 🌾 Committed to publishing high‑quality articles covering core technologies of leading internet firms, application architecture, and AI breakthroughs.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.