What is the preferred way to handle configuration parameters for a Go program (the kind of stuff one might use properties files or ini files for, in other contexts)?
-
1I also started a [golang-nuts thread](https://groups.google.com/forum/#!msg/golang-nuts/A5dScrKqgRo/33_Ymb8hGwUJ) which has a few additional ideas. – theglauber May 09 '13 at 22:02
-
3I tend to use shell scripts and environment variables. – Sep 12 '15 at 13:42
-
4I devoted a whole blog post [Persisting Application Configuration In Go](http://goinbigdata.com/persisting-application-configuration-in-golang/) where I explained how to do it with examples for two most popular formats: json and YAML. The examples are production ready. – upitau Jun 26 '16 at 12:22
-
1Just for the record there is HCL from HashiCorp which supports comments and is JSON and UCL compatible. https://github.com/hashicorp/hcl – Kaveh Shahbazian Feb 03 '17 at 20:22
-
1https://gobyexample.com/command-line-arguments – Simon Mulquin Jun 25 '18 at 21:14
-
https://github.com/eduardbcom/gonfig use gonfig – Eduard Bondarenko Jul 05 '18 at 20:25
-
By the way here is much more simple approach: https://gbws.io/articles/configuration-in-go/ – Sergey Kamardin Dec 10 '19 at 21:09
-
Another way to handle Go lang environment configs - https://github.com/sonyjop/go-konfigs – Sony Joseph Jan 24 '20 at 09:56
-
This package combines a simple interface with the power of tools like Viper: https://github.com/num30/config – Denis Palnitsky May 06 '22 at 08:27
13 Answers
The JSON format worked for me quite well. The standard library offers methods to write the data structure indented, so it is quite readable.
See also this golang-nuts thread.
The benefits of JSON are that it is fairly simple to parse and human readable/editable while offering semantics for lists and mappings (which can become quite handy), which is not the case with many ini-type config parsers.
Example usage:
conf.json:
{
"Users": ["UserA","UserB"],
"Groups": ["GroupA"]
}
Program to read the configuration
import (
"encoding/json"
"os"
"fmt"
)
type Configuration struct {
Users []string
Groups []string
}
file, _ := os.Open("conf.json")
defer file.Close()
decoder := json.NewDecoder(file)
configuration := Configuration{}
err := decoder.Decode(&configuration)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(configuration.Users) // output: [UserA, UserB]

- 10,681
- 20
- 92
- 192

- 55,207
- 13
- 135
- 135
-
Interesting. Better than using XML, imho. JSON may turn out be the best option currently available. Thanks for the golang-nuts pointer. I was amused once more by the hostility towards Yaml. – theglauber May 09 '13 at 16:22
-
2@theglauber Yaml is quite good for configuration files in terms of readability but not less error prone (e.g., tabs vs. spaces) and it seems quite hard to parse. Quite like XML, except that it doesn't even offer readability :). However, if you're interested in Yaml, you might want to play around with [go-yaml](http://code.google.com/p/goyaml/). – nemo May 09 '13 at 16:29
-
8It seems that JSON is the least bad of the current alternatives. I looked into go-yaml and it's a valiant effort, but i took the lack of documentation as an indication that i should look elsewhere. [goini](https://github.com/glacjay/goini) seems to be a simple and easy library to handle Windows _ini_ files. A new format called TOML has been proposed, but it also [has problems](https://news.ycombinator.com/item?id=5272634). At this point i would stick to JSON or _ini_. – theglauber May 09 '13 at 22:06
-
ack! in the end i used XML, due to lack of comments in JSON format. I could just as easily have done my own ini parser, but it was a way to learn Go's XML library. – theglauber May 14 '13 at 14:04
-
1Why didn't you add a `"comment"` key as suggested [here](https://groups.google.com/d/msg/golang-nuts/sAofmg2_lL8/_kwG7Y__N4AJ)? – nemo May 14 '13 at 14:09
-
1
-
2@theglauber, theoretically, yaml is a [superset of json](http://yaml.org/spec/1.2/spec.html#id2759572). – miku Jan 20 '14 at 16:03
-
8YAML supports comments, if you want to add notes everywhere in config file. – Ivan Black Sep 18 '14 at 09:19
-
1to me the main advantage of choosing JSON over the other alternatives is that you can do it without using any third party packages. Of course, I don't mean using third party packages is bad, but if you can avoid an additional dependency, that's a plus. – rob74 Jan 16 '15 at 11:28
-
72For those reading this and going down that route, beware: JSONs lack of comments makes it unsuitable for a human usable configuration file (imo). It is a data interchange format - you may find losing the ability to write helpful/descriptive comments in config files can hurt maintainability ("why is this setting activated?", "what does it do?", "what are valid values for it?" etc). – Darian Moody Jan 22 '15 at 02:04
-
8Ahhh - I tried that in my code and forgot to define the struct attributes with uppercase letters (not exported) - this cost me an hour of my life. Maybe others commit the same error > be warned ;D – JohnGalt Mar 02 '16 at 16:30
-
9
-
2
-
2If you need to add comments to a json file, just add them as a field:desc entry, and ignore it when importing. for example you can have a field called {notes:["note1","note2"]} – Cyberience Nov 01 '18 at 06:13
-
1A weird thing happened with me. Configurations fields are case sensitive and the should be Capitalized first letter, even though config file has all small cases. – Ankita Mehta Jun 17 '22 at 18:17
Another option is to use TOML, which is an INI-like format created by Tom Preston-Werner. I built a Go parser for it that is extensively tested. You can use it like other options proposed here. For example, if you have this TOML data in something.toml
Age = 198
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z
Then you can load it into your Go program with something like
type Config struct {
Age int
Cats []string
Pi float64
Perfection []int
DOB time.Time
}
var conf Config
if _, err := toml.DecodeFile("something.toml", &conf); err != nil {
// handle error
}

- 13,917
- 7
- 52
- 45
-
21I like TOML because it lets me write comments either on newlines or at the end of a line configuring setting. I can't do that with JSON. – sergserg May 22 '14 at 21:34
-
-
5Every approach to config does. How else would your program be aware of the new config? – BurntSushi5 May 23 '17 at 11:10
-
@BurntSushi5 can there be extra fields in the Toml file that the code does not care about? I mean, can a newer version of the config file be used with older version of code? It’s okay in my case to ignore unused config options. – user1952500 Jan 09 '18 at 18:05
-
Yes. It's not hard to try for yourself. Works the same way that JSON decoding works. – BurntSushi5 Jan 09 '18 at 22:01
-
3i like it. Good work. Personally i think it's easier for admins or customers to change a TOML file than a JSON. – blndev Jan 15 '18 at 21:07
Viper is a golang configuration management system that works with JSON, YAML, and TOML. It looks pretty interesting.

- 17,584
- 8
- 40
- 46
-
2
-
Use gonfig for JSON configuration in Go. https://github.com/eduardbcom/gonfig – Eduard Bondarenko Jul 05 '18 at 20:32
-
3
-
-
1@Dr.eel Try separate viper.GetBool("abc") and Viper.Set("abc", false) in different goroutine. – igonejack Jun 08 '19 at 08:06
-
4Your configuration should probably be loaded at the beginning of your program, and the values then passed to your dependencies. Loading configuration in separate goroutines is probably the wrong way to do it. I would never expect a config loading library to be safe for concurrent use. – Markus Dec 02 '20 at 10:50
-
1Though Viper has an explicit feature to write config changes made at runtime, and the ability to watch for config changes. It does not state where you should make these changes. – Arnold Schrijver Jan 18 '21 at 19:55
I usually use JSON for more complicated data structures. The downside is that you easily end up with a bunch of code to tell the user where the error was, various edge cases and what not.
For base configuration (api keys, port numbers, ...) I've had very good luck with the gcfg package. It is based on the git config format.
From the documentation:
Sample config:
; Comment line
[section]
name = value # Another comment
flag # implicit value for bool is true
Go struct:
type Config struct {
Section struct {
Name string
Flag bool
}
}
And the code needed to read it:
var cfg Config
err := gcfg.ReadFileInto(&cfg, "myconfig.gcfg")
It also supports slice values, so you can allow specifying a key multiple times and other nice features like that.

- 422
- 5
- 10

- 6,784
- 2
- 26
- 40
-
5The original author of [gcfg](https://code.google.com/p/gcfg/) discontinued the project and start another related one [sconf](https://github.com/sconf/sconf). – iwat May 22 '15 at 07:05
Just use standard go flags with iniflags.
go flags
import "flag" var nFlag = flag.Int("n", 1234, "help message for flag n")
iniflags
package main import ( "flag" ... "github.com/vharitonsky/iniflags" ... ) var ( flag1 = flag.String("flag1", "default1", "Description1") ... flagN = flag.Int("flagN", 123, "DescriptionN") ) func main() { iniflags.Parse() // use instead of flag.Parse() }
Standard go flags have the following benefits:
- Idiomatic.
- Easy to use. Flags can be easily added and scattered across arbitrary packages your project uses.
- Flags have out-of-the-box support for default values and description.
- Flags provide standard 'help' output with default values and description.
The only drawback standard go flags have - is management problems when the number of flags used in your app becomes too large.
Iniflags elegantly solves this problem: just modify two lines in your main package and it magically gains support for reading flag values from ini file. Flags from ini files can be overriden by passing new values in command-line.
See also https://groups.google.com/forum/#!topic/golang-nuts/TByzyPgoAQE for details.
-
I started using flags for a project I've been working on (my first from-scratch golang project), but I'm wondering how to handle things like tests? For example, this is an api client, and I'd like to use flags, but it seems like it would over complicate my testing (`go test` doesn't let me pass in flags) while a config file wouldn't. – zachaysan Jan 26 '15 at 17:43
-
-
12would be very helpful if there were detailed example code here showing a working example :) – zero_cool Oct 06 '15 at 17:58
-
1Not a good idea when you need to share config with other pieces of application written in another languages. – Kirzilla Mar 26 '16 at 20:53
-
1would suggest to use pflags instead of flags. pflags is using the posix-standard – Fjolnir Dvorak Jun 30 '17 at 10:35
-
Use gonfig for JSON configuration in Go. github.com/eduardbcom/gonfig – Eduard Bondarenko Jul 05 '18 at 20:33
I have started using Gcfg which uses Ini-like files. It's simple - if you want something simple, this is a good choice.
Here's the loading code I am using currently, which has default settings and allows command line flags (not shown) that override some of my config:
package util
import (
"code.google.com/p/gcfg"
)
type Config struct {
Port int
Verbose bool
AccessLog string
ErrorLog string
DbDriver string
DbConnection string
DbTblPrefix string
}
type configFile struct {
Server Config
}
const defaultConfig = `
[server]
port = 8000
verbose = false
accessLog = -
errorLog = -
dbDriver = mysql
dbConnection = testuser:TestPasswd9@/test
dbTblPrefix =
`
func LoadConfiguration(cfgFile string, port int, verbose bool) Config {
var err error
var cfg configFile
if cfgFile != "" {
err = gcfg.ReadFileInto(&cfg, cfgFile)
} else {
err = gcfg.ReadStringInto(&cfg, defaultConfig)
}
PanicOnError(err)
if port != 0 {
cfg.Server.Port = port
}
if verbose {
cfg.Server.Verbose = true
}
return cfg.Server
}

- 9,714
- 5
- 34
- 50
have a look at gonfig
// load
config, _ := gonfig.FromJson(myJsonFile)
// read with defaults
host, _ := config.GetString("service/host", "localhost")
port, _ := config.GetInt("service/port", 80)
test, _ := config.GetBool("service/testing", false)
rate, _ := config.GetFloat("service/rate", 0.0)
// parse section into target structure
config.GetAs("service/template", &template)

- 2,985
- 1
- 26
- 28
-
This one is good, since I don't have to redefine entire config structure in go – thanhpk Dec 13 '16 at 16:43
https://github.com/spf13/viper and https://github.com/zpatrick/go-config are a pretty good libraries for configuration files.

- 1,262
- 12
- 16
I wrote a simple ini config library in golang.
goroutine-safe, easy to use
package cfg
import (
"testing"
)
func TestCfg(t *testing.T) {
c := NewCfg("test.ini")
if err := c.Load() ; err != nil {
t.Error(err)
}
c.WriteInt("hello", 42)
c.WriteString("hello1", "World")
v, err := c.ReadInt("hello", 0)
if err != nil || v != 42 {
t.Error(err)
}
v1, err := c.ReadString("hello1", "")
if err != nil || v1 != "World" {
t.Error(err)
}
if err := c.Save(); err != nil {
t.Error(err)
}
}
===================Update=======================
Recently I need an INI parser with section support, and I write a simple package:
github.com/c4pt0r/cfg
u can parse INI like using "flag" package:
package main
import (
"log"
"github.com/c4pt0r/ini"
)
var conf = ini.NewConf("test.ini")
var (
v1 = conf.String("section1", "field1", "v1")
v2 = conf.Int("section1", "field2", 0)
)
func main() {
conf.Parse()
log.Println(*v1, *v2)
}

- 51
- 1
- 3
You might also be interested in go-libucl, a set of Go bindings for UCL, the Universal Configuration Language. UCL is a bit like JSON, but with better support for humans: it supports comments and human-readable constructs like SI multipliers (10k, 40M, etc.) and has a little bit less boilerplate (e.g., quotes around keys). It's actually pretty close to the nginx configuration file format, if you're already familiar with that.

- 68
- 6
I agree with nemo and I wrote a little tool to make it all real easy.
bitbucket.org/gotamer/cfg is a json configuration package
- You define your config items in your application as a struct.
- A json config file template from your struct is saved on the first run
- You can save runtime modifications to the config
See doc.go for an example
I tried JSON. It worked. But I hate having to create the struct of the exact fields and types I might be setting. To me that was a pain. I noticed it was the method used by all the configuration options I could find. Maybe my background in dynamic languages makes me blind to the benefits of such verboseness. I made a new simple config file format, and a more dynamic-ish lib for reading it out.
https://github.com/chrisftw/ezconf
I am pretty new to the Go world, so it might not be the Go way. But it works, it is pretty quick, and super simple to use.
Pros
- Super simple
- Less code
Cons
- No Arrays or Map types
- Very flat file format
- Non-standard conf files
- Does have a little convention built-in, which I now if frowned upon in general in Go community. (Looks for config file in the config directory)