Secrets, Properly
The Secret Leak Ladder (Worst → Best)
A secret is any value that grants access if someone else gets it - a database password, an API key, a signing certificate. Where you put a secret determines exactly who ends up able to read it, from "anyone on the internet who ever pulls this image" down to "only the specific process that needs it, for as briefly as possible":
| # | 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.