A small, zero-dependency Go library for loading typed configuration from
.env-style files into structs, with real OS environment variables
available as a fallback.
It has two parts:
- A hand-written
.envfile parser (Parse) with no third-party dependencies — comments, blank lines, quoted and unquoted values. - A reflection-based struct loader (
Load) that binds parsed values (and, as a fallback, real OS environment variables) onto struct fields usingenv:"KEY_NAME"tags, and reports every validation problem it finds in a single call instead of stopping at the first one.
go get github.com/kasapdev/go-envconfigGiven a .env file like this:
# .env
APP_NAME=widget-service
APP_PORT=8080
APP_DEBUG=true
APP_TAGS=api,internal,betapackage main
import (
"fmt"
"log"
"github.com/kasapdev/go-envconfig"
)
type Config struct {
Name string `env:"APP_NAME,required"`
Port int `env:"APP_PORT,required"`
Debug bool `env:"APP_DEBUG"`
Tags []string `env:"APP_TAGS"`
}
func main() {
var cfg Config
if err := envconfig.Load(".env", &cfg); err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", cfg)
// {Name:widget-service Port:8080 Debug:true Tags:[api internal beta]}
}For each tagged field, Load resolves its value in this order:
- The parsed
.envfile is checked first. - If the key is not present in the file,
Loadfalls back to the real OS environment (os.Getenv). - If the key is present in neither, the field is left at its zero value —
unless the tag includes
,required, in which case it is recorded as a missing-field error.
This means a .env file can be used to override the OS environment during
local development, while deployment environments that don't ship a .env
file at all can still supply every value through real environment
variables.
Reads a .env-style file and returns its key/value pairs as a map. Skips
full-line comments (#...) and blank lines. Supports:
- Double-quoted values, with
\n,\t,\r,\", and\\escapes processed. - Single-quoted values, used verbatim (no escape processing).
- Unquoted values, trimmed of surrounding whitespace.
Parse does not touch the real process environment — it only returns the
parsed map. It is exposed directly in case you want the raw key/value pairs
without struct binding.
Parses the file at path and populates the exported fields of target
(which must be a non-nil pointer to a struct) using env:"KEY_NAME" struct
tags:
env:"KEY_NAME"— optional field.env:"KEY_NAME,required"— the key must resolve to a value (from the file or the OS environment); otherwise it's reported as an error.
Supported field types: string, int, int64, bool, and []string
(the raw value is split on commas, with each element trimmed of
whitespace).
Load never stops at the first problem. It collects every missing
required field and every type-conversion failure, then returns them all
together as a single *MultiError. It returns nil if and only if no
problems were found.
Implements error, and aggregates multiple underlying errors:
Error() string— renders one problem per line.Unwrap() []error— supportserrors.Is/errors.Asper the Go 1.20+ multi-error convention.Errors() []error— returns the individual errors.Len() int— the number of aggregated errors.
go test ./...MIT — see LICENSE.