TechLead
Lesson 14 of 18
5 min read
Docker & DevOps

Environment Variables & Configuration

Manage application configuration across environments using Docker environment variables, secrets, and config files

Configuration in Docker

Applications should be configured through the environment, not hardcoded values. This follows the 12-Factor App methodology and makes your containers portable across development, staging, and production environments.

Setting Environment Variables

In docker run

# Individual variables
docker run -e NODE_ENV=production -e PORT=3000 myapp

# From a file
docker run --env-file .env myapp

# From host environment
export API_KEY=sk-123
docker run -e API_KEY myapp  # Passes host's API_KEY

In Dockerfile

# Set default values (can be overridden at runtime)
ENV NODE_ENV=production
ENV PORT=3000

# Use build arguments for build-time variables
ARG VERSION=1.0.0
ENV APP_VERSION=${VERSION}

# ❌ NEVER put secrets in Dockerfile
# ENV API_KEY=sk-secret-key

In Docker Compose

# docker-compose.yml
services:
  api:
    build: .
    environment:
      # Direct values
      NODE_ENV: production
      PORT: "4000"

      # From .env file variables
      DATABASE_URL: postgres://${DB_USER}:${DB_PASS}@db:5432/${DB_NAME}
      REDIS_URL: redis://redis:6379

    # Load from .env file
    env_file:
      - .env
      - .env.local    # Overrides (not committed to git)

The .env File

# .env — Docker Compose auto-loads this
NODE_ENV=production
PORT=4000

# Database
DB_USER=admin
DB_PASS=supersecret
DB_NAME=myapp
DB_HOST=db
DB_PORT=5432

# External services
REDIS_URL=redis://redis:6379
API_KEY=sk-your-api-key

# App settings
LOG_LEVEL=info
CORS_ORIGIN=https://myapp.com

⚠️ Security Warning

Never commit .env files with real secrets to version control!

# .gitignore
.env
.env.local
.env.production
*.pem

Instead, commit a .env.example with placeholder values:

# .env.example — commit this
NODE_ENV=development
DB_USER=admin
DB_PASS=change-me
API_KEY=your-api-key-here

Multi-Environment Configuration

# docker-compose.yml (base)
services:
  api:
    build: .
    env_file: .env

# docker-compose.dev.yml
services:
  api:
    env_file:
      - .env
      - .env.development
    volumes:
      - ./src:/app/src

# docker-compose.prod.yml
services:
  api:
    env_file:
      - .env
      - .env.production
    restart: always
# Run with specific environment
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Accessing Environment Variables in Code

// Node.js
const config = {
  port: parseInt(process.env.PORT || '3000'),
  nodeEnv: process.env.NODE_ENV || 'development',
  databaseUrl: process.env.DATABASE_URL,
  redisUrl: process.env.REDIS_URL,
  apiKey: process.env.API_KEY,
};

// Validate required variables
const required = ['DATABASE_URL', 'API_KEY'];
for (const key of required) {
  if (!process.env[key]) {
    console.error(`Missing required environment variable: ${key}`);
    process.exit(1);
  }
}

Key Takeaways

  • ✅ Use environment variables for all configuration — never hardcode
  • ✅ Use .env files for local development
  • ✅ Never commit secrets to version control
  • ✅ Validate required environment variables at startup
  • ✅ Use different compose override files for each environment

Continue Learning