Reading Environment Variables in Go
Use os.Getenv() to read an environment variable:
package main
import (
"fmt"
"os"
)
func main() {
val := os.Getenv("MY_VAR")
fmt.Println(val)
}
os.Getenv() returns an empty string if the variable doesn’t exist. This means you can’t distinguish between an unset variable and one explicitly set to an empty string.
Checking if a variable exists
To verify whether a variable is actually set, use the two-value return form:
package main
import (
"fmt"
"os"
)
func main() {
val, exists := os.Getenv("MY_VAR")
if !exists {
fmt.Println("MY_VAR not set")
return
}
fmt.Println("MY_VAR:", val)
}
This pattern is critical for required configuration — fail fast if essential variables are missing rather than silently using empty strings.
Getting all variables
Retrieve all environment variables as a slice of KEY=VALUE strings:
package main
import (
"fmt"
"os"
)
func main() {
for _, env := range os.Environ() {
fmt.Println(env)
}
}
Loading from .env files
For local development, use the godotenv package to load variables from a .env file before your application starts:
package main
import (
"fmt"
"log"
"os"
"github.com/joho/godotenv"
)
func main() {
err := godotenv.Load(".env")
if err != nil {
log.Fatal("Error loading .env file")
}
dbHost := os.Getenv("DB_HOST")
fmt.Println("DB_HOST:", dbHost)
}
Call godotenv.Load() early in main(), before any code that depends on those variables. In production, skip this step entirely — environment variables should be injected by your orchestration layer.
Type conversion and defaults
Environment variables are always strings. Convert them as needed:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
portStr := os.Getenv("PORT")
if portStr == "" {
portStr = "8080"
}
port, err := strconv.Atoi(portStr)
if err != nil {
fmt.Println("Invalid PORT:", err)
return
}
fmt.Printf("Listening on port %d\n", port)
}
For more complex defaults and type coercion, consider a config library like viper or envconfig.
Production patterns
In Kubernetes and cloud environments, inject variables via:
- ConfigMaps: For non-sensitive configuration
- Secrets: For credentials and tokens
- System environment: Set directly via deployment manifests or container runtime
Your Go code remains unchanged — os.Getenv() works identically whether the variable comes from a .env file, Docker environment flag, or Kubernetes Secret.
A common pattern for required configuration:
package main
import (
"fmt"
"log"
"os"
)
func getEnv(key string) string {
val, exists := os.Getenv(key)
if !exists {
log.Fatalf("Required environment variable not set: %s", key)
}
return val
}
func main() {
apiKey := getEnv("API_KEY")
dbURL := getEnv("DATABASE_URL")
fmt.Printf("Configured with API key and database\n")
}
This approach fails immediately if critical configuration is missing, preventing runtime surprises in production.