Secrets Management
Hardcoding passwords in docker-compose.yml or passing them as environment variables is the #1 security mistake in Docker deployments. Anyone with access to docker inspect can read them.
The Problem with Environment Variables
# DON'T do this
docker run -e DB_PASSWORD=super-secret-123 postgres
Why it is dangerous:
docker inspectshows all environment variables in plain text- The password is stored in shell history (
.bash_history) - Container orchestrators may log environment variables
# Anyone can read secrets from a running container
docker inspect my-db --format '{{json .Config.Env}}'
Solution: File-Based Secrets
The industry standard is to mount secrets as files into the container. Many official images support this with the _FILE suffix:
services:
db:
image: postgres:16
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt # Local file containing the password
flowchart LR
File["secrets/db_password.txt<br/>(on host, gitignored)"] -->|"Mounted as"| Mount["/run/secrets/db_password<br/>(inside container)"]
Mount -->|"Read by"| App["PostgreSQL<br/>(POSTGRES_PASSWORD_FILE)"]
style File fill:#e3f2fd,stroke:#1565c0
style Mount fill:#fff3e0,stroke:#ef6c00
style App fill:#e8f5e9,stroke:#2e7d32
Setup Steps
-
Create the secret file:
mkdir -p secrets
echo "my-strong-password" > secrets/db_password.txt -
Add
secrets/to.gitignore:secrets/ -
Images that support
_FILEsuffix: PostgreSQL, MySQL, MariaDB, Redis (with config), and most official database images.
Docker Swarm Secrets (Encrypted at Rest)
If using Docker Swarm, secrets are encrypted and distributed only to nodes that need them:
# Create a secret from stdin
echo "my-strong-password" | docker secret create db_password -
# Or from a file
docker secret create db_password ./secrets/db_password.txt
Reference in Compose:
secrets:
db_password:
external: true # Already created via `docker secret create`
Secrets Decision Guide
| Environment | Recommended Approach |
|---|---|
| Local development | .env file (gitignored) |
| Single-host production | Compose secrets: with file-based secrets |
| Swarm | docker secret create (encrypted at rest) |
| Kubernetes | Kubernetes Secrets or external vault |
| Enterprise | HashiCorp Vault, AWS Secrets Manager, etc. |
What NOT to Do
| Anti-Pattern | Risk |
|---|---|
Password in docker-compose.yml | Committed to git, visible to everyone with repo access |
Password in Dockerfile ENV | Baked into every image layer, visible with docker history |
Password in -e flag | Visible in docker inspect, shell history, process list |
Password in .env committed to git | Exposed in version control history forever |
Key Takeaways
- Never hardcode secrets in Compose files, Dockerfiles, or shell commands.
- Use file-based secrets (the
secrets:directive in Compose) for production. - Many official images support the
_FILEsuffix pattern (e.g.,POSTGRES_PASSWORD_FILE). - Use
.envfiles for development, but always gitignore them. - For production, use an external secret manager (Vault, AWS SM) or Docker Swarm secrets.
What's Next
- Continue to Security Audit Checklist for a pre-deployment security review.