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":

#PatternWho can read it
1COPY .env into the imageanyone who can pull the image, forever
2ENV DB_PASSWORD=… in the Dockerfilesame - it's baked into image metadata
3-e DB_PASSWORD=… at run timeanyone with docker inspect rights; often leaks into logs & crash dumps
4File mounted read-only + *_FILE envonly processes inside the container
5Secret manager (Vault, cloud SM) fetched at startupnarrowest: 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.