DevDocsDev Docs
Docker Compose

Environment Configuration

Managing environment variables, secrets, and configuration in Docker Compose

Docker Compose provides multiple ways to configure your application through environment variables, env files, and secrets. Understanding these options is crucial for secure and flexible deployments.

Environment Variables

Setting Environment Variables

services:
  api:
    image: myapi:latest
    environment:
      # Key-value pairs
      - NODE_ENV=production
      - PORT=3000
      - DATABASE_URL=postgres://db:5432/app
      
      # Or as a map
      # environment:
      #   NODE_ENV: production
      #   PORT: 3000
services:
  api:
    image: myapi:latest
    env_file:
      # Single file
      - .env
      
      # Multiple files (loaded in order)
      - .env
      - .env.local
      - .env.${ENVIRONMENT:-development}
# .env
NODE_ENV=production
DATABASE_URL=postgres://user:pass@db:5432/app
API_KEY=secret123
# Pass from shell
export DATABASE_URL=postgres://localhost:5432/app
docker compose up
# Reference in compose file
services:
  api:
    environment:
      # Pass through from shell
      - DATABASE_URL
      
      # With default value
      - PORT=${PORT:-3000}

Variable Substitution

Use variables in your Compose file with shell-style syntax.

services:
  web:
    image: myapp:${TAG:-latest}
    ports:
      - "${HOST_PORT:-8080}:${CONTAINER_PORT:-80}"
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - LOG_LEVEL=${LOG_LEVEL:-info}

Substitution Syntax

SyntaxDescription
${VAR}Value of VAR, error if unset
${VAR:-default}Value of VAR, or default if unset/empty
${VAR-default}Value of VAR, or default if unset
${VAR:?error}Value of VAR, error with message if unset/empty
${VAR?error}Value of VAR, error with message if unset
services:
  db:
    image: postgres:${POSTGRES_VERSION:-16}-alpine
    environment:
      # Required variable
      - POSTGRES_PASSWORD=${DB_PASSWORD:?Database password required}
      
      # Optional with default
      - POSTGRES_USER=${DB_USER:-postgres}
      - POSTGRES_DB=${DB_NAME:-app}

The .env File

The .env file in the same directory as docker-compose.yml is automatically loaded.

# .env
COMPOSE_PROJECT_NAME=myproject
TAG=v1.2.3
NODE_ENV=production
DATABASE_URL=postgres://user:password@db:5432/myapp

# These are used for variable substitution in compose file
HOST_PORT=8080
POSTGRES_VERSION=16

Security

Never commit .env files with secrets to version control. Add .env to .gitignore.

Multiple Environment Files

services:
  api:
    env_file:
      - .env                    # Base configuration
      - .env.${ENV:-dev}        # Environment-specific
      - .env.local              # Local overrides (gitignored)
# .env.dev
DATABASE_URL=postgres://localhost:5432/app_dev
DEBUG=true

# .env.prod
DATABASE_URL=postgres://prod-db:5432/app
DEBUG=false

Secrets Management

For sensitive data, use Docker secrets instead of environment variables.

services:
  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt
# ./secrets/db_password.txt
mysupersecretpassword
services:
  api:
    secrets:
      - api_key
      - db_password

secrets:
  api_key:
    external: true
  db_password:
    external: true
    name: production_db_password
# Create external secrets
echo "secret123" | docker secret create api_key -
echo "dbpass" | docker secret create production_db_password -
services:
  api:
    image: myapi:latest
    secrets:
      - source: api_key
        target: /run/secrets/api_key
        uid: '1000'
        gid: '1000'
        mode: 0400
// Reading secret in application
const fs = require('fs');
const apiKey = fs.readFileSync('/run/secrets/api_key', 'utf8').trim();

Configuration Files

Use configs for non-sensitive configuration files.

services:
  nginx:
    image: nginx:alpine
    configs:
      - source: nginx_config
        target: /etc/nginx/nginx.conf

configs:
  nginx_config:
    file: ./nginx.conf

Environment by Profile

Use profiles to manage environment-specific services.

services:
  api:
    image: myapi:latest
    environment:
      - NODE_ENV=production

  api-dev:
    image: myapi:latest
    profiles:
      - development
    environment:
      - NODE_ENV=development
      - DEBUG=true
    volumes:
      - .:/app

  db:
    image: postgres:16-alpine
    
  adminer:
    image: adminer
    profiles:
      - development
    ports:
      - "8080:8080"
# Production (default)
docker compose up -d

# Development with debug tools
docker compose --profile development up -d

Environment Patterns

Development vs Production

# docker-compose.yml
services:
  api:
    build: .
    environment:
      - NODE_ENV
      - DATABASE_URL
      - REDIS_URL
    depends_on:
      - db
      - redis

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_PASSWORD=${DB_PASSWORD}

  redis:
    image: redis:7-alpine
# docker-compose.override.yml (auto-loaded)
services:
  api:
    build:
      target: development
    volumes:
      - .:/app
      - /app/node_modules
    environment:
      - NODE_ENV=development
      - DEBUG=true
    ports:
      - "3000:3000"
      - "9229:9229"  # Debug port

  db:
    ports:
      - "5432:5432"
# docker-compose.prod.yml
services:
  api:
    build:
      target: production
    environment:
      - NODE_ENV=production
    env_file:
      - .env.production
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: '1'
          memory: 1G

  db:
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_password:
    external: true

12-Factor App Pattern

services:
  api:
    image: myapi:${VERSION:-latest}
    environment:
      # All config via environment
      - PORT=${PORT:-3000}
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
      - LOG_LEVEL=${LOG_LEVEL:-info}
      - API_KEY=${API_KEY}
    
    # Or use env_file for cleaner compose file
    env_file:
      - .env.${ENVIRONMENT:-development}

Best Practices

Debugging Environment

# View effective configuration
docker compose config

# View environment for a service
docker compose exec api env

# Check variable substitution
docker compose config | grep -A5 "environment"

# Run with debug output
docker compose --verbose up

Common Issues

IssueCauseSolution
Variable not substitutedNot in shell or .envAdd to .env or export
Empty variableUnset in shellUse :-default syntax
Secret not foundWrong pathCheck mount at /run/secrets
Config not loadingWrong file nameMust be exact .env

On this page