Configuration management in Go (using Viper)

January 14, 2017

Config factor from The Twelve-Factor App states that application’s configuration should be isolated from code to make configuring an app for different environments very convenient and scaling it up a breeze.

Configuration management for modern applications which run on so many different environment is becoming complex with advent of microservices and cloud computing. Not to mention dealing with several formats/markup languages to store app’s configurations.

This post is about one of my favorite Go libraries Viper which claims to be a complete configuration solution for Go applications. Among other features, it supports (as of this writing) reading configuration from

  • JSON, TOML, YAML, HCL, and Java properties config files
  • Environment variables
  • Commandline flags
  • Remote config systems (etcd or Consul)

Let’s see how to use Viper to take advantage of some of these features.

Install Viper

go get

Read from config file

While it supports various file formats, we’ll use an example JSON configuration below.


    "prod": {
        "host": "",
        "port": "8081",
        "enabled": true
    "dev": {
        "host": "",
        "port": "8082",
        "enabled": true
    "qa": {
        "host": "",
        "port": "8083",
        "enabled": true

Note: Viper does not require any initialization before using, unless we’ll be dealing multiple different configurations. check working with multiple vipers

Set config file we want to read

There are 2 ways to do this. Set path including config file name and extension


Or Set path(s) to loook for config files in.

// Add paths. Accepts multiple paths. It will search these paths in given order
// And then register config file name (no extension)
// Optionally we can set specific config type
// viper.SetConfigType("json")

Read the config file in viper

// Searches for config file in given paths and read it
if err := viper.ReadInConfig(); err != nil {
    log.Fatalf("Error reading config file, %s", err)

// Confirm which config file is used
fmt.Printf("Using config: %s\n", viper.ConfigFileUsed())

Get values from viper

Get method can retrieve any value given the key (case-insensitive) to use. Get returns an interface. For a specific value use one of the Get____ methods (e.g. GetInt, GetBool etc)

port := viper.Get("prod.port") // returns string
//port := viper.GetInt("prod.port") // returns integer
fmt.Printf("Value: %v, Type: %T\n", port, port)

Check if a particular key is set using IsSet

if !viper.IsSet("prod.port") {
    log.Fatal("missing port number")

Notice that we can trverse nested configuration using a . delimited path e.g. prod.port

Read values in struct

Instead of reading keys one by one we can extract sub-tree using Sub and decode it in struct using Unmarshal

prod := viper.Sub("prod")

// Unmarshal into struct. Struct fields should match keys from config (case in-sensitive)
type config struct {
    Host    string
    Port    int
    enabled bool

var C config

err := prod.Unmarshal(&C)
if err != nil {
    log.Fatalf("unable to decode into struct, %v", err)

Read from Env variables [TODO]

You can find full source of this example in github repo

This post gives just a glimpse of Viper’s capabilities. It’s worth exploring some of the interesting things it offers like,

comments powered by Disqus