Parse environment variables into Go structs with minimal boilerplate and first-class support for complex data structures
- Field names just work (snake or camel)
- Nested structs automatically create prefixes
- No special tags needed
- When field name doesn't match, tries existing json/yaml/toml etc.. tags
- Explicit env tags available and take highest precedence, but rarely needed
- One string value per variable
- No need to encode complex data in environment values
- Maps:
SETTINGS_KEY=value
- Arrays:
PORTS_0=8080
- Nested structs:
REDIS_HOST=localhost
- Slice of structs:
SERVERS_0_HOST=host1
- Map of structs:
DATABASES_PRIMARY_HOST=host1
- Most features from popular libraries available
- No lock-in to specific tag formats, tags are all configurable
- Default settings are all configurable
- Parsers/decoders are all configurable
go get github.com/sethpollack/envcfg
Set the following environment variables:
# string
export NAME=name
# integer
export PORT=8080
# float
export RATE=1.23
# boolean
export IS_ENABLED=true
# duration
export TIMEOUT=60s
# nested struct
export REDIS_HOST=localhost
export REDIS_PORT=6379
# delimited slice
export TAGS=tag1,tag2,tag3
# indexed slice
export PORTS_0=8080
export PORTS_1=9090
# slice of structs
export SERVERS_0_HOST=localhost1
export SERVERS_0_PORT=8080
export SERVERS_1_HOST=localhost2
export SERVERS_1_PORT=9090
# key-value map
export LABELS=key1:value1,key2:value2
# flat map
export SETTINGS_KEY1=1
export SETTINGS_KEY2=2
# map of structs
export DATABASES_PRIMARY_HOST=localhost1
export DATABASES_PRIMARY_PORT=6379
export DATABASES_SECONDARY_HOST=localhost2
export DATABASES_SECONDARY_PORT=6380
Parse the environment variables into a struct:
package main
import (
"fmt"
"time"
"github.com/sethpollack/envcfg"
)
func main() {
type ServerConfig struct {
Host string
Port int
}
type Config struct {
Name string
Port int
Rate float64
IsEnabled bool
Timeout time.Duration
Redis ServerConfig
Tags []string
Ports []int
Servers []ServerConfig
Labels map[string]string
Settings map[string]int
Databases map[string]ServerConfig
}
cfg := Config{}
if err := envcfg.Parse(&cfg); err != nil {
panic(err)
}
fmt.Printf(`Config:
Name: %s
Port: %d
Rate: %.2f
IsEnabled: %t
Timeout: %s
Redis:
Host: %s
Port: %d
Tags: %v
Ports: %v
Servers: [
{Host: %s, Port: %d},
{Host: %s, Port: %d}
]
Labels: %v
Settings: %v
Databases: {
primary: {Host: %s, Port: %d},
secondary: {Host: %s, Port: %d}
}
`,
cfg.Name, cfg.Port, cfg.Rate, cfg.IsEnabled, cfg.Timeout,
cfg.Redis.Host, cfg.Redis.Port,
cfg.Tags, cfg.Ports,
cfg.Servers[0].Host, cfg.Servers[0].Port,
cfg.Servers[1].Host, cfg.Servers[1].Port,
cfg.Labels, cfg.Settings,
cfg.Databases["primary"].Host, cfg.Databases["primary"].Port,
cfg.Databases["secondary"].Host, cfg.Databases["secondary"].Port,
)
}
Results:
Config:
Name: name
Port: 8080
Rate: 1.23
IsEnabled: true
Timeout: 1m0s
Redis:
Host: localhost
Port: 6379
Tags: [tag1 tag2 tag3]
Ports: [8080 9090]
Servers: [
{Host: localhost1, Port: 8080},
{Host: localhost2, Port: 9090}
]
Labels: map[key1:value1 key2:value2]
Settings: map[key1:1 key2:2]
Databases: {
primary: {Host: localhost1, Port: 6379},
secondary: {Host: localhost2, Port: 6380}
}
Tip
The example above demonstrates the three supported syntaxes for both slices and maps:
- Slices:
- delimited
TAGS=tag1,tag2,tag3
- indexed
PORTS_0=8080
- struct
SERVERS_0_HOST=localhost
- delimited
- Maps:
- key-value pairs
LABELS=key1:value1
- flat keys
SETTINGS_KEY1=1
- struct
DATABASES_PRIMARY_HOST=localhost
- key-value pairs
string
int
,int8
,int16
,int32
,int64
uint
,uint8
,uint16
,uint32
,uint64
bool
float32
,float64
time.Duration
structs
slices
maps
Note
Type support can be extended using the WithKindParser
and WithTypeParser
options.
envcfg.Decoder
flag.Value
encoding.TextUnmarshaler
encoding.BinaryUnmarshaler
Note
Decoder support can be extended using the WithDecoder
option.
Name | Description | Default | Example Tag | Example Option |
---|---|---|---|---|
default |
Default value when environment variable is not set | - | default:"8080" |
env:",default=8080" |
required |
Mark field as required | false |
required:"true" |
env:",required" |
notempty |
Ensure value is not empty | false |
notempty:"true" |
env:",notempty" |
expand |
Expand environment variables in value | false |
expand:"true" |
env:",expand" |
file |
Load value from file | false |
file:"true" |
env:",file" |
delim |
Delimiter for array values | , |
delim:";" |
env:",delim=;" |
sep |
Separator for map key-value pairs | : |
sep:"=" |
env:",sep=" |
init |
Initialize nil pointers | vars |
init:"always" |
env:",init=always" |
ignore |
Ignore field | false |
ignore:"true" |
env:",ignore" or env:"-" |
decodeunset |
Decode unset environment variables | false |
decodeunset:"true" |
env:",decodeunset" |
Warning
When setting default values for slices, avoid using the comma as it conflicts with tag parsing. Either use a different delimiter or set array values using environment variables:
type Config struct {
Tags []string `env:"TAGS,default=tag1,tag2"` // This will fail!
}
// Do this instead:
type Config struct {
Tags []string `env:"TAGS,delim=;,default=tag1;tag2"` // Use a different delimiter
}
vars
- Initialize when values are present with the exception of structs that only have default values. (default)any
- Same asvars
, but also initialize structs that only have default values.always
- Always initializenever
- Never initialize
Tip
All defaults and tag names can be customized using the With*
options. See Configuration Options for more details.
By default, envcfg
will search for environment variables using multiple naming patterns until a match is found. Use WithDisableFallback
to restrict matching to only the env
tag value.
For example:
os.Setenv("CUSTOM_URL", "value") // Matches env tag
os.Setenv("DATABASEURL", "value") // Matches struct field
os.Setenv("DATABASE_URL", "value") // Matches snake-case
os.Setenv("DB_URL", "value") // Matches json tag
os.Setenv("DATA_SOURCE", "value") // Matches yaml tag
// ...
type Config struct {
DatabaseURL string `json:"db_url" yaml:"data_source" env:"custom_url"`
}
Tip
All environment variable matching is case insensitive.
Parse
- Parse environment variables into a struct pointerMustParse
- Same asParse
, but panics on errorParseAs
- Parse environment variables into a specific typeMustParseAs
- Same asParseAs
, but panics on error
Important
envcfg
only parses exported fields.
Option | Description | Default |
---|---|---|
WithTagName |
Tag name for environment variables | env |
WithDelimiterTag |
Tag name for delimiter | delim |
WithSeparatorTag |
Tag name for separator | sep |
WithDecodeUnsetTag |
Tag name for decoding unset environment variables | decodeunset |
WithDefaultTag |
Tag name for default values | default |
WithExpandTag |
Tag name for expandable variables | expand |
WithFileTag |
Tag name for file variables | file |
WithNotEmptyTag |
Tag name for not empty variables | notempty |
WithRequiredTag |
Tag name for required variables | required |
WithIgnoreTag |
Tag name for ignored variables | ignore |
WithInitTag |
Tag name for initialization strategy | init |
Option | Description | Default |
---|---|---|
WithDelimiter |
Sets the default delimiter for array and map values | , |
WithSeparator |
Sets the default separator for map key-value pairs | : |
WithDecodeUnset |
Enables decoding unset environment variables by default | false |
WithInitAny |
Sets the initialization strategy to any |
vars |
WithInitNever |
Sets the initialization strategy to never |
vars |
WithInitAlways |
Sets the initialization strategy to always |
vars |
WithExpand |
Enables environment variable expansion by default | false |
WithNotEmpty |
Enables validating that values are not empty by default | false |
WithRequired |
Enables marking fields as required by default | false |
WithDisableFallback |
Enables strict matching using the env tag |
false |
Option | Description |
---|---|
WithTypeParser |
Registers a custom type parser |
WithTypeParsers |
Registers custom type parsers |
WithKindParser |
Registers a custom kind parser |
WithKindParsers |
Registers custom kind parsers |
Option | Description |
---|---|
WithDecoder |
Registers a custom decoder function for a specific interface |
Option | Description |
---|---|
WithLoader |
Registers a loader |
Loader options configure how environment variables are loaded and filtered before being matched to struct fields. These options allow you to:
- Add different sources of configuration (environment variables, files, external services)
- Filter which environment variables are considered
- Transform environment variable names before matching
- Set default values for when environment variables are not found
Option | Description |
---|---|
WithSource |
Adds a source to the loader |
WithSources |
Adds multiple sources to the loader |
WithOSEnvSource |
Adds OS environment variables as a source |
WithMapEnvSource |
Uses the provided map of environment variables as a source |
WithDotEnvSource |
Adds environment variables from a file as a source |
WithPrefix |
Combines WithTrimPrefix and WithHasPrefix |
WithSuffix |
Combines WithTrimSuffix and WithHasSuffix |
WithTransform |
Adds a transform function that modifies environment variable keys |
WithTrimPrefix |
Removes a prefix from environment variable names |
WithTrimSuffix |
Removes a suffix from environment variable names |
WithFilter |
Adds a custom filter to the loader |
WithHasPrefix |
Adds a prefix filter to the loader |
WithHasSuffix |
Adds a suffix filter to the loader |
WithHasMatch |
Adds a pattern filter to the loader |
WithKeys |
Adds a filter to the loader that only includes specific keys |
envcfg supports multiple configuration sources that can be used individually or combined. Built-in sources have dedicated With*
functions, while custom sources and those maintained as separate Go modules (to keep dependencies isolated) can be added using WithSource
.
Option | Description |
---|---|
WithOSEnvSource |
Adds OS environment variables as a source |
WithMapEnvSource |
Uses the provided map of environment variables as a source |
WithDotEnvSource |
Adds environment variables from a .env file as a source |
WithSource(awssm.New(...)) |
Adds AWS Secrets Manager as a source |
Sources are processed in the order they are added, with later sources taking precedence over earlier ones. This ordering allows you to:
- Set default values by adding a source with defaults first
- Override values by adding sources with higher precedence later
- Create a hierarchy of configuration (e.g., defaults → shared config → environment-specific config → local overrides)
envcfg.Parse(&cfg,
envcfg.WithLoader(
envcfg.WithMapEnvSource(map[string]string{
"APP_PORT": "8080",
"LOG_LEVEL": "info",
}),
envcfg.WithDotEnvSource(".env"),
envcfg.WithSource(awssm.New(
awssm.WithRegion("us-west-2"),
awssm.WithSecretID("myapp/config"),
)),
envcfg.WithOSEnvSource(),
),
)
In this setup:
- Default values are loaded first
- Then values from the .env file are loaded next
- Then values from AWS Secrets Manager
- Finally, OS environment variables take precedence over both defaults and .env file