Passing sensitive data (API keys, database passwords) into containers requires care — naive approaches like hardcoding in a Dockerfile or exposing in environment variables have real security drawbacks. This guide covers better approaches.
What NOT to Do
# BAD - never do this
ENV DATABASE_PASSWORD=mysecretpassword
Hardcoding secrets directly in a Dockerfile means they're baked into the image itself, potentially visible to anyone with access to the image (including in image history/layers even if later "removed").
Environment Variables: Better, But Not Perfect
docker run -e DATABASE_PASSWORD=mysecretpassword myapp
Better than hardcoding in the image, but environment variables can be visible via docker inspect, process listings, and are sometimes accidentally logged — a meaningful improvement over image-baked secrets, but not the strongest option for genuinely sensitive data.
Using .env Files (Convenient, Requires Care)
docker compose --env-file .env up -d
See Docker Compose .env Files and Environment Variables Explained — convenient for local development; ensure .env is in .gitignore and never committed to version control.
Docker Secrets (Swarm Mode)
echo "mysecretpassword" | docker secret create db_password -
services:
app:
secrets:
- db_password
secrets:
db_password:
external: true
Secrets are mounted as files inside the container (typically under /run/secrets/), not exposed as environment variables — genuinely more secure, but requires Docker Swarm mode, not plain Docker Compose.
Reading a Secret Inside the Application
const password = fs.readFileSync('/run/secrets/db_password', 'utf8').trim();
Using a Dedicated Secrets Manager (Best for Production)
For genuinely production-grade secret management, consider a dedicated secrets management tool (HashiCorp Vault or similar) that your application fetches secrets from at runtime — provides audit logging, rotation capabilities, and centralized access control beyond what Docker's built-in mechanisms offer alone.
Using Cloud Provider Secret Managers
If using cloud-managed database or infrastructure services, their native secret management integrations are often a convenient middle ground between simple environment variables and running your own dedicated secrets infrastructure.
Mounting Secrets as Files via Bind Mount (Simple Alternative Without Swarm)
services:
app:
volumes:
- /secure/path/db_password:/run/secrets/db_password:ro
A simpler pattern achieving similar file-based secret access without requiring Swarm mode — ensure the host-side file has appropriately restrictive permissions.
Never Commit Secrets to Version Control
echo ".env" >> .gitignore
echo "secrets/" >> .gitignore
A fundamental practice regardless of which secret management approach you use — see How to Manage Environment Variables and Secrets on a VPS for the broader principle.
Rotating Secrets
Whatever approach you use, ensure you have a practical process for rotating secrets periodically or after a suspected exposure — secrets that are difficult to rotate tend to never actually get rotated in practice.
Common Errors
Secret accidentally visible in image layers — if a secret was ever included during a build step (even if later removed in a subsequent instruction), it may still exist in earlier image layers; rebuild without ever including the secret in the first place, and rotate the exposed credential.
Continue Reading
- How to Manage Environment Variables and Secrets on a VPS
- Docker Compose .env Files and Environment Variables Explained
- Docker Security Best Practices for Production Servers
Browse more articles in Docker & Containers.