Environment Variables and Configuration Management
Complete guide to managing application configuration securely across development, staging, and production environments
Environment variables are the cornerstone of modern application configuration. They enable the same codebase to run in different environments while keeping sensitive information secure. Whether you're deploying to Docker containers, Kubernetes clusters, or traditional servers, proper environment variable management is crucial for security, scalability, and maintainability.
This comprehensive guide covers everything from basic .env file management to advanced secrets handling in production environments, helping you build robust configuration systems that grow with your application.
Table of Contents
- 1. Environment Variables Fundamentals
- 2. .env Files and Local Development
- 3. Security Best Practices
- 4. Docker Configuration
- 5. Kubernetes Secrets Management
- 6. Cloud Platform Configuration
- 7. Configuration Validation
- 8. Deployment Strategies
1. Environment Variables Fundamentals
What Are Environment Variables?
Environment variables are key-value pairs that exist outside your application code but can be accessed by your application at runtime. They provide a way to configure your application without hardcoding values into your source code.
Why Use Environment Variables?
- Security
Keep sensitive data like API keys and passwords out of source code
- Flexibility
Same codebase runs in different environments with different configurations
- Deployment
Change configuration without rebuilding or redeploying code
- Compliance
Meet security standards by separating config from code
Common Environment Variables
# Application Configuration NODE_ENV=production PORT=3000 APP_URL=https://myapp.com # Database Configuration DATABASE_URL=postgresql://user:pass@localhost:5432/mydb REDIS_URL=redis://localhost:6379 # API Keys and Secrets JWT_SECRET=your-super-secure-secret-key STRIPE_SECRET_KEY=sk_live_... SENDGRID_API_KEY=SG.abc123... # External Services AWS_ACCESS_KEY_ID=AKIA... AWS_SECRET_ACCESS_KEY=... AWS_REGION=us-east-1
2. .env Files and Local Development
Creating .env Files
.env files provide a convenient way to manage environment variables during development. They should be in your project root and follow this format:
# .env file structure # Comments start with # DATABASE_URL=postgresql://localhost:5432/myapp_dev API_KEY=dev-api-key-123 DEBUG=true # Use quotes for values with spaces APP_NAME="My Awesome App" # Multi-line values use escaped newlines PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7...\n-----END PRIVATE KEY-----"
Environment-Specific Files
Create separate .env files for different environments:
- .env
Default values and non-sensitive configuration
- .env.local
Local development overrides (gitignored)
- .env.development
Development environment specific
- .env.production
Production environment template
3. Security Best Practices
Identifying Sensitive Variables
Some environment variables contain sensitive information that requires special handling:
🔐 Sensitive
• API keys and tokens
• Database passwords
• JWT secrets
• Private keys
• OAuth credentials
✅ Safe
• Application names
• Public URLs
• Feature flags
• Timeout values
• Debug settings
Secret Management Best Practices
- Use a secrets manager
AWS Secrets Manager, Azure Key Vault, HashiCorp Vault
- Rotate secrets regularly
Implement automatic rotation for API keys and tokens
- Principle of least privilege
Grant minimal access required for each environment
- Audit access
Log and monitor who accesses sensitive configuration
4. Docker Configuration
Docker Environment Variables
Docker provides several ways to pass environment variables to containers:
# Method 1: Command line
docker run -e NODE_ENV=production -e PORT=3000 myapp
# Method 2: Environment file
docker run --env-file .env.production myapp
# Method 3: Docker Compose
version: '3.8'
services:
app:
build: .
environment:
- NODE_ENV=production
- PORT=3000
env_file:
- .env.production
ports:
- "3000:3000"Docker Secrets
For sensitive data in Docker Swarm, use Docker secrets:
# Create a secret
echo "my-secret-password" | docker secret create db_password -
# Use in docker-compose.yml
version: '3.8'
services:
app:
image: myapp
secrets:
- db_password
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
db_password:
external: true5. Kubernetes Secrets Management
Kubernetes ConfigMaps vs Secrets
ConfigMaps
For non-sensitive configuration data like application settings, feature flags, and public configuration.
Secrets
For sensitive data like passwords, API keys, and certificates. Base64 encoded and encrypted at rest.
Creating Kubernetes Secrets
# Create secret from literal values kubectl create secret generic app-secrets \ --from-literal=database-password=secretpassword \ --from-literal=api-key=abc123 # Create secret from .env file kubectl create secret generic app-config --from-env-file=.env.production # Secret YAML manifest apiVersion: v1 kind: Secret metadata: name: app-secrets type: Opaque data: database-password: c2VjcmV0cGFzc3dvcmQ= # base64 encoded api-key: YWJjMTIz # base64 encoded
Using Secrets in Deployments
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: app
image: myapp:latest
env:
# From ConfigMap
- name: NODE_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: environment
# From Secret
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: app-secrets
key: database-password
# Mount entire secret as volume
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
volumes:
- name: secret-volume
secret:
secretName: app-secrets6. Cloud Platform Configuration
AWS Configuration
- AWS Systems Manager Parameter Store
Hierarchical storage with encryption and versioning
- AWS Secrets Manager
Automatic rotation and fine-grained access control
- ECS Task Definitions
Environment variables in container definitions
- Lambda Environment Variables
Built-in encryption with AWS KMS
Azure Configuration
- Azure Key Vault
Centralized secrets and certificate management
- App Configuration
Feature flags and dynamic configuration
- Container Apps Environment Variables
Secret and non-secret configuration
7. Configuration Validation
Runtime Validation
Always validate environment variables at application startup:
// Node.js validation example
const requiredEnvVars = [
'DATABASE_URL',
'JWT_SECRET',
'STRIPE_SECRET_KEY'
];
function validateEnvironment() {
const missing = requiredEnvVars.filter(varName => !process.env[varName]);
if (missing.length > 0) {
console.error('Missing required environment variables:', missing);
process.exit(1);
}
// Validate formats
if (process.env.PORT && isNaN(parseInt(process.env.PORT))) {
console.error('PORT must be a number');
process.exit(1);
}
if (process.env.DATABASE_URL && !process.env.DATABASE_URL.startsWith('postgresql://')) {
console.error('DATABASE_URL must be a valid PostgreSQL connection string');
process.exit(1);
}
}
// Run validation on startup
validateEnvironment();Configuration Schema
Define a schema for your configuration to catch errors early:
// Using Joi for validation
const Joi = require('joi');
const configSchema = Joi.object({
NODE_ENV: Joi.string().valid('development', 'production', 'test').required(),
PORT: Joi.number().port().default(3000),
DATABASE_URL: Joi.string().uri().required(),
JWT_SECRET: Joi.string().min(32).required(),
REDIS_URL: Joi.string().uri().optional(),
LOG_LEVEL: Joi.string().valid('error', 'warn', 'info', 'debug').default('info')
});
const { error, value } = configSchema.validate(process.env, {
allowUnknown: true,
abortEarly: false
});
if (error) {
console.error('Configuration validation error:', error.details);
process.exit(1);
}
module.exports = value;8. Deployment Strategies
Configuration Deployment Pipeline
- Development
Local .env files with default values
- Staging
CI/CD pipeline injects staging secrets
- Production
Secrets manager or encrypted storage
GitOps Configuration Management
# Repository structure for GitOps config/ ├── base/ │ ├── configmap.yaml │ └── kustomization.yaml ├── environments/ │ ├── development/ │ │ ├── configmap.yaml │ │ ├── secrets.yaml (encrypted) │ │ └── kustomization.yaml │ ├── staging/ │ │ ├── configmap.yaml │ │ ├── secrets.yaml (encrypted) │ │ └── kustomization.yaml │ └── production/ │ ├── configmap.yaml │ ├── secrets.yaml (encrypted) │ └── kustomization.yaml
Zero-Downtime Configuration Updates
- Rolling Updates
Gradually replace instances with new configuration
- Configuration Reload
Hot-reload configuration without restart (where possible)
- Canary Deployments
Test configuration changes with subset of traffic
- Rollback Strategy
Quick rollback mechanism for configuration issues
Conclusion
Effective environment variable management is crucial for secure, scalable applications. By following best practices for local development, implementing proper security measures, and using the right tools for each deployment environment, you can build robust configuration systems that support your application's growth.
Remember that configuration management is an ongoing process. Regularly audit your environment variables, rotate secrets, and update your configuration strategy as your application and infrastructure evolve.
Related Tools
• Environment Variables Manager - Validate and manage .env files
• Kubernetes YAML Generator - Create ConfigMaps and Secrets
• Base64 Encoder/Decoder - Encode secrets for Kubernetes
• Hash Generator - Generate secure secret values
• Password Generator - Create strong passwords for configs