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
# In terminal
go get github.com/spf13/viper2.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:
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: trafficThis 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:
white_list:
user_id: # User ID list
- 063105015
- 063105024
- 063105028
request_path: # Access paths
- /api/v1/users
- /api/v1/ops2.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
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"`
}2.4.2 custom.go
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"`
}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.
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)2.5 Key step: struct mapping logic
The core parsing logic resides in bootstrap/config.go:
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
", 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)
}2.6 Overall file structure
2.7 Running the program
main.go loads the configuration and prints it:
package main
import (
"fmt"
"traffic.demo/global"
)
func main() {
bootstrap.InitializeConfig()
fmt.Println("Traffic Service Started...!")
fmt.Printf("globalCong: %+v
", global.App.Config)
fmt.Printf("customCong: %+v
", global.App.Custom)
}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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
