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/envcfgSet 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=6380Parse 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
stringint,int8,int16,int32,int64uint,uint8,uint16,uint32,uint64boolfloat32,float64time.Durationstructsslicesmaps
Note
Type support can be extended using the WithKindParser and WithTypeParser options.
envcfg.Decoderflag.Valueencoding.TextUnmarshalerencoding.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