config

config provides configuration bootstrap for Go services built on top of Viper.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/config"

Package config provides configuration bootstrap for Go services built on top of Viper.

It keeps configuration loading predictable across local development, CI, and deployment without repeating boilerplate in every application, centralizing:

  • default values
  • local file discovery
  • environment overrides
  • optional file-less configuration via environment data (the “envvar” provider)
  • optional pluggable remote sources via WithRemoteLoader
  • final schema validation

An application integrates by implementing Configuration:

  • SetDefaults(v Viper) to register application-specific defaults
  • Validate() error to enforce final constraints

This is a Viper-based implementation of the configuration model described in: Nicola Asuni, 2014-09-13, “Software Configuration” https://technick.net/guides/software/software_configuration/

Configuration load order

The effective configuration is built in this order (later steps override earlier ones):

  1. Built-in defaults from this package (log and shutdown settings), plus application defaults from SetDefaults.
  2. Local config file (default: config.json) searched in the explicit configDir (if provided) first, and then in: ./, $HOME/./, /etc//
  3. Environment variables for remote source selection: _REMOTECONFIGPROVIDER, _REMOTECONFIGENDPOINT, _REMOTECONFIGPATH, _REMOTECONFIGSECRETKEYRING, _REMOTECONFIGDATA.
  4. Remote configuration loading, when configured:
    • provider “envvar”: decodes base64 JSON from REMOTECONFIGDATA
    • any other provider: delegated to the application-supplied RemoteLoaderFunc registered with the WithRemoteLoader option
  5. Environment variables are also applied on the final Viper instance, so they can override file/remote values (useful for secrets and runtime overrides). Only keys registered with a default (via SetDefaults) or present in the config file/remote source are candidates for environment overrides: a key that has no default and appears nowhere else is not populated from the environment. Register a default for every configurable key to make it reliably env-overridable.
  6. Validate() is called on the final decoded config struct.

For a complete implementation example, see the Configuration implementation in examples/service/internal/cli/config.go and the Load call in examples/service/internal/cli/cli.go

When To Use

  • Configuration comes from several layers with a defined precedence.
  • The same binary runs locally, in CI, and in production with different sources.
  • The fully merged configuration should be validated before the service starts.

Example

// A real service ships config.json alongside the binary, or relies on
// the search path: ./, $HOME/.<cmdName>/, /etc/<cmdName>/.
dir, err := os.MkdirTemp("", "nurago-config-example")
if err != nil {
	fmt.Println(err)

	return
}

defer func() { _ = os.RemoveAll(dir) }()

file := `{"server_address":":9090","max_workers":8}`

err = os.WriteFile(filepath.Join(dir, "config.json"), []byte(file), 0o600)
if err != nil {
	fmt.Println(err)

	return
}

// Environment variables override the file, which overrides the
// defaults. The prefix keeps the service's variables namespaced.
_ = os.Setenv("EXAMPLESRV_MAX_WORKERS", "16")

defer func() { _ = os.Unsetenv("EXAMPLESRV_MAX_WORKERS") }()

cfg := &appConfig{}

err = config.Load("examplesrv", dir, "EXAMPLESRV", cfg)
if err != nil {
	fmt.Println(err)

	return
}

fmt.Println(cfg.ServerAddress, cfg.MaxWorkers, cfg.Log.Level)

// Output:
// :9090 16 info

Full source is in example_config_test.go. More runnable examples are on pkg.go.dev.

Dependencies

Importing this package pulls 12 external modules:

  • github.com/fsnotify/fsnotify
  • github.com/go-viper/mapstructure/v2
  • github.com/pelletier/go-toml/v2
  • github.com/sagikazarmark/locafero
  • github.com/spf13/afero
  • github.com/spf13/cast
  • github.com/spf13/pflag
  • github.com/spf13/viper
  • github.com/subosito/gotenv
  • go.yaml.in/yaml/v3
  • golang.org/x/sys
  • golang.org/x/text