Skip to content

File Format

The env library supports multiple configuration file formats: .env, JSON, and YAML.

.env Format

Basic Syntax

bash
# Comment
KEY=value

# Equals sign in value
URL=https://example.com?foo=bar

# Empty lines are ignored

# Invalid: keys cannot have spaces
# MY KEY=value

Quotes

bash
# Double quotes: preserve spaces, support escapes
MESSAGE="Hello World"
PATH="/usr/local/bin"

# Single quotes: no escape processing (backslash sequences preserved as-is)
# Note: single quotes do not prevent variable expansion — expansion happens after quote stripping
LITERAL='no escaping here: \n stays literal'

# No quotes
SIMPLE=value

# Empty values
EMPTY=
EMPTY=""
EMPTY=''

Escape Characters

Escapes are supported in double quotes:

bash
# Newline
MULTILINE="line1\nline2"

# Tab
TABBED="col1\tcol2"

# Quotes
QUOTED="He said \"Hello\""

# Backslash
PATH="C:\\Users\\name"

# Dollar sign
PRICE="Price: \$100"

Variable Expansion

Supported when ExpandVariables is enabled:

bash
# Reference other variables
BASE_URL=https://api.example.com
API_URL=${BASE_URL}/v1

# Simple syntax
URL=$BASE_URL/path

# Default values
HOST=${HOST:-localhost}
PORT=${PORT:-8080}

# Nested expansion
SERVICE=${CLUSTER:-default}-${REGION:-us-east}

export Syntax

Supported when AllowExportPrefix is enabled:

bash
# Bash-style export
export KEY=value
export ANOTHER="quoted value"

YAML Style

Supported when AllowYamlSyntax is enabled:

bash
# YAML-style key-value pairs
KEY: value
ANOTHER: "quoted value"

Multi-line Values

The .env parser scans line by line, parsing each line independently, and does not support quoted strings spanning multiple lines — double-quoted values must close on a single line, otherwise ErrInvalidValue is returned. For line breaks, use \n escapes (only effective in double quotes; single quotes don't process escapes):

bash
# \n inside double quotes is parsed as a newline character
LINES="line1\nline2\nline3"
# The actual value is three lines of text: line1 / line2 / line3

# Multi-line certificates like PRIVATE_KEY should be joined with \n
PRIVATE_KEY="-----BEGIN KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...\n-----END KEY-----"

For true multi-line strings, use JSON or YAML format instead, or extend multi-line support via a custom parser.

JSON Format

Basic Structure

json
{
    "APP_NAME": "my-app",
    "APP_VERSION": "1.0.0",
    "DEBUG": true,
    "PORT": 8080
}

Nested Objects

Nested objects are flattened:

json
{
    "database": {
        "host": "localhost",
        "port": 5432
    }
}

Result:

text
DATABASE_HOST=localhost
DATABASE_PORT=5432

Arrays

Arrays are flattened to indexed keys:

json
{
    "ALLOWED_HOSTS": ["localhost", "example.com"],
    "PORTS": [80, 443, 8080]
}

Result:

text
ALLOWED_HOSTS_0=localhost
ALLOWED_HOSTS_1=example.com
PORTS_0=80
PORTS_1=443
PORTS_2=8080

TIP

Use the GetSlice[T] function or dot-path to access indexed keys:

go
hosts := env.GetSlice[string]("ALLOWED_HOSTS")
port0 := env.GetInt("PORTS_0")  // 80

See GetSlice documentation.

Type Conversion Options

go
cfg := env.DefaultConfig()

// null converts to empty string
cfg.JSONNullAsEmpty = true

// numbers convert to strings
cfg.JSONNumberAsString = true

// booleans convert to strings
cfg.JSONBoolAsString = true

Depth Limit

go
cfg.JSONMaxDepth = 10  // Max nesting depth

YAML Format

Basic Structure

yaml
APP_NAME: my-app
APP_VERSION: "1.0.0"
DEBUG: true
PORT: 8080

Nested Structures

yaml
database:
  host: localhost
  port: 5432
  credentials:
    user: admin
    password: secret

Flattened result:

text
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_CREDENTIALS_USER=admin
DATABASE_CREDENTIALS_PASSWORD=secret

Lists

Lists are flattened to indexed keys:

yaml
allowed_hosts:
  - localhost
  - example.com
  - api.example.com

Result:

text
ALLOWED_HOSTS_0=localhost
ALLOWED_HOSTS_1=example.com
ALLOWED_HOSTS_2=api.example.com

Multi-line Strings

WARNING

YAML block scalars (literal block | and folded block >) are currently not supported. The parser treats |/> as ordinary scalar characters, and subsequent indented lines will break key-value parsing.

For values that need to preserve line breaks, use double quotes with \n escapes:

yaml
description: "Line1\nLine2\nLine3"

Or extend block scalar support via a custom parser.

Type Conversion Options

go
cfg := env.DefaultConfig()

cfg.YAMLNullAsEmpty = true
cfg.YAMLNumberAsString = true
cfg.YAMLBoolAsString = true
cfg.YAMLMaxDepth = 10

Format Detection

Auto-detection

go
// Detect by extension
format := env.DetectFormat("config.json")   // FormatJSON
format = env.DetectFormat("settings.yaml")  // FormatYAML
format = env.DetectFormat(".env")           // FormatEnv

// Returns FormatAuto when no matching extension (defaults to .env parser)
format = env.DetectFormat("config")  // FormatAuto

Format Constants

go
const (
    FormatAuto  FileFormat = iota  // Auto-detect
    FormatEnv                      // .env format
    FormatJSON                     // JSON format
    FormatYAML                     // YAML format
)

Format String

go
format := env.FormatJSON
fmt.Println(format.String())  // Output: json

Best Practices

Choosing a Format

ScenarioRecommended Format
Simple configuration.env
Complex nested configurationJSON or YAML
Sharing with other toolsJSON
Human readability priorityYAML
Docker/K8s environment.env

File Naming

bash
.env              # Default configuration
.env.local        # Local overrides (not committed)
.env.development  # Development environment
.env.staging      # Staging environment
.env.production   # Production environment
.env.test         # Test environment

Mixed Usage

go
// You can mix different formats
loader.LoadFiles(
    "base.env",           // Base configuration
    "database.json",      // Database configuration
    "secrets.yaml",       // Sensitive configuration
    ".env.local",         // Local overrides
)

Git Ignore

bash
# Ignore sensitive configuration
.env.local
.env.*.local
.env.production
secrets.yaml

# Keep templates
!.env.example