Secrets, Properly
The Secret Leak Ladder (Worst → Best)
| # | Pattern | Who can read it |
|---|---|---|
| 1 | COPY .env into the image | anyone who can pull the image, forever |
| 2 | ENV DB_PASSWORD=… in the Dockerfile | same - it's baked into image metadata |
| 3 | -e DB_PASSWORD=… at run time | anyone with docker inspect rights; often leaks into logs & crash dumps |
| 4 | File mounted read-only + *_FILE env | only processes inside the container |
| 5 | Secret manager (Vault, cloud SM) fetched at startup | narrowest: short-lived, audited, rotatable |
Level 4 is the pattern every orchestrator standardized on (Swarm secrets, Kubernetes secret volumes):
$ echo "$DB_PASS" > /srv/secrets/db_password && chmod 600 /srv/secrets/db_password
$ docker run -d \
-v /srv/secrets/db_password:/run/secrets/db_password:ro \
-e DB_PASSWORD_FILE=/run/secrets/db_password \
crm-api:v3
The _FILE convention (supported by the official postgres, mysql, redis images and easy to add to your own apps): if X_FILE is set, read the value from that path instead of X*.
Scenario: Real incident shape: a crash reporter uploads the process environment with every stack trace. Env-var secrets ship themselves to a third-party SaaS on every crash. File-based secrets don't. That's the Secrets Off the Env lab.
Tip: Also rotate what you can't hide: even with perfect hygiene, treat any secret that ever touched an env var, a log line or an image layer as burned.