Skip to main content

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 inspect shows 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

  1. Create the secret file:

    mkdir -p secrets
    echo "my-strong-password" > secrets/db_password.txt
  2. Add secrets/ to .gitignore:

    secrets/
  3. Images that support _FILE suffix: 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

EnvironmentRecommended Approach
Local development.env file (gitignored)
Single-host productionCompose secrets: with file-based secrets
Swarmdocker secret create (encrypted at rest)
KubernetesKubernetes Secrets or external vault
EnterpriseHashiCorp Vault, AWS Secrets Manager, etc.

What NOT to Do

Anti-PatternRisk
Password in docker-compose.ymlCommitted to git, visible to everyone with repo access
Password in Dockerfile ENVBaked into every image layer, visible with docker history
Password in -e flagVisible in docker inspect, shell history, process list
Password in .env committed to gitExposed 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 _FILE suffix pattern (e.g., POSTGRES_PASSWORD_FILE).
  • Use .env files for development, but always gitignore them.
  • For production, use an external secret manager (Vault, AWS SM) or Docker Swarm secrets.

What's Next