Echo Environment Variables
Introduction
Environment variables are a crucial aspect of modern application deployment. They allow you to configure your Echo applications without changing code, making your applications more portable, secure, and easier to manage across different environments.
In this guide, we'll explore how to effectively use environment variables in your Echo applications, why they matter for deployment, and best practices for managing configuration.
Why Use Environment Variables?
Before diving into implementation, let's understand why environment variables are important:
- Separation of configuration from code: Following the twelve-factor app methodology
- Security: Keeping sensitive information out of your codebase
- Environment-specific configuration: Easily switching between development, testing, and production settings
- Containerization support: Essential for Docker and Kubernetes deployments
Accessing Environment Variables in Echo
Echo applications can access environment variables through Go's standard library. Here's how to do it:
package main
import (
"fmt"
"os"
"github.com/labstack/echo/v4"
)
func main() {
e := echo.New()
// Get an environment variable with a default fallback
port := os.Getenv("PORT")
if port == "" {
port = "8080" // Default port if not specified
}
e.GET("/", func(c echo.Context) error {
return c.String(200, "Server running with configuration from environment variables")
})
e.Logger.Fatal(e.Start(":" + port))
}
In this example, the application reads the PORT
environment variable to determine which port to use. If the variable isn't set, it defaults to 8080
.
Setting Environment Variables
There are multiple ways to set environment variables depending on your development and deployment environment.
Local Development
For local development, you can set variables in your terminal session:
Linux/macOS:
export PORT=3000
export DB_CONNECTION_STRING="postgresql://user:pass@localhost:5432/mydb"
Windows (Command Prompt):
set PORT=3000
set DB_CONNECTION_STRING=postgresql://user:pass@localhost:5432/mydb
Windows (PowerShell):
$env:PORT = "3000"
$env:DB_CONNECTION_STRING = "postgresql://user:pass@localhost:5432/mydb"
Using .env Files
A common practice is to use .env
files for local development. You'll need the godotenv
package:
go get github.com/joho/godotenv
Now you can create a .env
file in your project root:
PORT=3000
DB_CONNECTION_STRING=postgresql://user:pass@localhost:5432/mydb
DEBUG=true
And load it in your application:
package main
import (
"fmt"
"os"
"github.com/joho/godotenv"
"github.com/labstack/echo/v4"
)
func main() {
// Load .env file if it exists
godotenv.Load()
e := echo.New()
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
debug := os.Getenv("DEBUG") == "true"
if debug {
e.Debug = true
}
e.GET("/", func(c echo.Context) error {
return c.String(200, "Server running with configuration from .env")
})
e.Logger.Fatal(e.Start(":" + port))
}
Important: Never commit
.env
files to version control if they contain sensitive information. Add.env
to your.gitignore
file.
Configuration Best Practices
Creating a Configuration Package
For larger applications, it's a good practice to centralize environment variable handling in a dedicated package:
// config/config.go
package config
import (
"os"
"strconv"
)
type Config struct {
Port string
DatabaseURL string
LogLevel string
Debug bool
JWTSecret string
}
func Load() *Config {
config := &Config{
Port: getEnv("PORT", "8080"),
DatabaseURL: getEnv("DATABASE_URL", ""),
LogLevel: getEnv("LOG_LEVEL", "info"),
Debug: getEnvAsBool("DEBUG", false),
JWTSecret: getEnv("JWT_SECRET", "default-secret"),
}
return config
}
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
func getEnvAsBool(key string, defaultValue bool) bool {
valueStr := getEnv(key, "")
if valueStr == "" {
return defaultValue
}
value, err := strconv.ParseBool(valueStr)
if err != nil {
return defaultValue
}
return value
}
Now you can use this config package in your main application:
// main.go
package main
import (
"your-app/config"
"github.com/joho/godotenv"
"github.com/labstack/echo/v4"
)
func main() {
// Load .env file for local development
godotenv.Load()
// Load configuration from environment
cfg := config.Load()
e := echo.New()
e.Debug = cfg.Debug
// Use configuration values throughout your app
e.GET("/", func(c echo.Context) error {
return c.String(200, "Server configured via environment")
})
e.Logger.Fatal(e.Start(":" + cfg.Port))
}
Environment Variables in Production Deployment
When deploying Echo applications to production, you'll set environment variables differently based on your deployment platform.
Docker Deployment
For Docker, you can set environment variables in your Dockerfile
or when running containers:
# Dockerfile
FROM golang:1.19-alpine AS build
# ... build steps ...
FROM alpine:latest
# ... copy binary from build stage ...
ENV PORT=8080
ENV LOG_LEVEL=info
# For sensitive values, don't hardcode them in the Dockerfile
CMD ["./myapp"]
When running the container:
docker run -e PORT=8080 -e DATABASE_URL="postgresql://user:pass@db:5432/mydb" -e JWT_SECRET="my-secret" myapp
Kubernetes Deployment
In Kubernetes, you can set environment variables in your deployment YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-app
spec:
# ...
template:
spec:
containers:
- name: echo-app
image: myapp:latest
env:
- name: PORT
value: "8080"
- name: LOG_LEVEL
value: "info"
# Reference secrets for sensitive values
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database_url
Cloud Providers
Most cloud platforms provide ways to configure environment variables:
- Heroku: Through the dashboard or CLI (
heroku config:set VAR=value
) - AWS Elastic Beanstalk: In the configuration settings
- Google Cloud Run: In the deployment configuration
- Azure App Service: In the Application Settings section
Environment Variables and Secrets
For sensitive information like API keys, database passwords, and JWT secrets, you should use a secure method to manage these values:
- Never commit secrets to version control
- Use your platform's secrets management:
- Kubernetes Secrets
- AWS Secrets Manager
- Google Secret Manager
- Azure Key Vault
Real-World Example: Configurable Echo API Server
Here's a comprehensive example of an Echo API server that uses environment variables for configuration:
package main
import (
"context"
"os"
"os/signal"
"time"
"github.com/joho/godotenv"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
type Config struct {
Port string
AllowedOrigins []string
RateLimit int
Timeout time.Duration
LogRequests bool
}
func loadConfig() Config {
// Load .env file if present (for local development)
godotenv.Load()
rateLimit, _ := strconv.Atoi(getEnv("RATE_LIMIT", "100"))
timeout, _ := strconv.Atoi(getEnv("TIMEOUT_SECONDS", "30"))
return Config{
Port: getEnv("PORT", "8080"),
AllowedOrigins: strings.Split(getEnv("ALLOWED_ORIGINS", "*"), ","),
RateLimit: rateLimit,
Timeout: time.Duration(timeout) * time.Second,
LogRequests: getEnv("LOG_REQUESTS", "true") == "true",
}
}
func getEnv(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
func main() {
// Load configuration from environment variables
config := loadConfig()
// Create Echo instance
e := echo.New()
// Configure middleware based on environment variables
if config.LogRequests {
e.Use(middleware.Logger())
}
e.Use(middleware.Recover())
e.Use(middleware.CORS(middleware.CORSConfig{
AllowOrigins: config.AllowedOrigins,
}))
e.Use(middleware.RateLimiter(middleware.NewRateLimiterMemoryStore(config.RateLimit)))
e.Use(middleware.TimeoutWithConfig(middleware.TimeoutConfig{
Timeout: config.Timeout,
}))
// Routes
e.GET("/", func(c echo.Context) error {
return c.JSON(200, map[string]string{
"message": "Echo API configured via environment variables",
})
})
// Start server with graceful shutdown
go func() {
if err := e.Start(":" + config.Port); err != nil {
e.Logger.Info("shutting down the server")
}
}()
// Wait for interrupt signal to gracefully shut down the server
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := e.Shutdown(ctx); err != nil {
e.Logger.Fatal(err)
}
}
Summary
Environment variables are an essential tool for configuring Echo applications across different environments. They help maintain the separation between code and configuration, improve security, and make your applications more flexible and portable.
Key takeaways:
- Use environment variables to store configuration that varies between environments
- Keep sensitive information like API keys and passwords in environment variables, not in code
- Provide sensible defaults for non-critical configuration
- For local development, use
.env
files but don't commit them to version control - Centralize environment variable handling in a dedicated configuration package
- For production, use the appropriate method to set environment variables based on your deployment platform
Additional Resources
- The Twelve-Factor App - Configuration
- Echo Framework Documentation
- godotenv Package Documentation
- Go's os.Getenv Documentation
Exercises
-
Create a simple Echo application that reads different database URLs based on an
ENVIRONMENT
variable that can bedevelopment
,testing
, orproduction
. -
Extend the configuration package to include validation for required environment variables, returning errors when critical variables are missing.
-
Create a middleware that adds environment-specific headers to responses based on configuration from environment variables.
-
Build an Echo application that supports different logging levels (debug, info, warning, error) configurable through an environment variable.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)