Common Misconfiguration
Embedding passwords, API keys, and other sensitive data directly in docker-compose.yml files exposes secrets in version control and makes rotation difficult.Vulnerable Example
Copy
# Vulnerable docker-compose.yml
version: '3.8'
services:
app:
image: myapp:1.0
environment:
DATABASE_URL: postgresql://admin:SuperSecret123!@db:5432/myapp # Hardcoded credentials
API_KEY: sk-proj-abc123def456ghi789 # Exposed API key
JWT_SECRET: my-super-secret-jwt-key-123 # Hardcoded secret
AWS_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE # AWS credentials
AWS_SECRET_ACCESS_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
ports:
- "3000:3000"
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: admin # Hardcoded username
POSTGRES_PASSWORD: SuperSecret123! # Hardcoded password
POSTGRES_DB: myapp
Secure Example
Copy
# Secure docker-compose.yml
version: '3.8'
services:
app:
image: myapp:1.0
environment:
DATABASE_URL_FILE: /run/secrets/database_url
JWT_SECRET_FILE: /run/secrets/jwt_secret
secrets:
- database_url
- api_key
- jwt_secret
- aws_credentials
ports:
- "3000:3000"
configs:
- source: app_config
target: /app/config.yml
mode: 0440
db:
image: postgres:16-alpine
environment:
POSTGRES_USER_FILE: /run/secrets/db_user
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
POSTGRES_DB: myapp
secrets:
- db_user
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
secrets:
database_url:
file: ./secrets/database_url.txt
api_key:
file: ./secrets/api_key.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
aws_credentials:
file: ./secrets/aws_credentials.json
db_user:
file: ./secrets/db_user.txt
db_password:
file: ./secrets/db_password.txt
configs:
app_config:
file: ./configs/app_config.yml
volumes:
postgres_data:
driver: local
driver_opts:
type: none
o: bind
device: /secure/data/postgres
Using External Secret Management
Copy
# Using Docker Swarm secrets
version: '3.8'
services:
app:
image: myapp:1.0
secrets:
- source: db_password
target: database_password
mode: 0400
deploy:
replicas: 3
restart_policy:
condition: on-failure
secrets:
db_password:
external: true # Created with: docker secret create db_password -
Environment File Approach
Copy
# .env.example (committed to repo)
DATABASE_HOST=db
DATABASE_PORT=5432
DATABASE_NAME=myapp
APP_PORT=3000
# .env.secret (NOT committed, in .gitignore)
DATABASE_USER=admin
DATABASE_PASSWORD=use_a_strong_password_here
JWT_SECRET=generate_a_random_secret
API_KEY=your_actual_api_key
Copy
# docker-compose.yml using env files
version: '3.8'
services:
app:
image: myapp:1.0
env_file:
- .env.example
- .env.secret # Contains sensitive values
ports:
- "${APP_PORT}:${APP_PORT}"
HashiCorp Vault Integration
Copy
# Using Vault Agent for secret injection
version: '3.8'
services:
vault-agent:
image: vault:1.15
command: ["vault", "agent", "-config=/vault/config/agent.hcl"]
volumes:
- ./vault-config:/vault/config:ro
- shared-secrets:/vault/secrets
environment:
VAULT_ADDR: https://vault.example.com:8200
app:
image: myapp:1.0
depends_on:
- vault-agent
volumes:
- shared-secrets:/secrets:ro
command: ["/bin/sh", "-c", "source /secrets/env && exec node app.js"]
volumes:
shared-secrets:
driver: local
driver_opts:
type: tmpfs
device: tmpfs

