Runtime secrets, done right
Get secrets into a read-only container without env leaks.
You have a non-root, read-only, distroless container — now it needs a database password. How you deliver it decides whether all that hardening holds. The wrong way is the common way: an environment variable. Env vars leak through docker inspect, the process environment (readable at /proc/<pid>/environ), child processes, crash dumps, and logs — so a secret passed with -e is exposed to anyone who can look at the container, hardened or not.
# why -e is unsafe: the secret is visible several ways at once$ docker run -d --name app -e DB_PASSWORD=S3cr3t myapp:1.0$ docker inspect app --format '{{json .Config.Env}}'["DB_PASSWORD=S3cr3t", ...] # right there in inspect$ sudo cat /proc/$(docker inspect -f {{.State.Pid}} app)/environ | tr '\0' '\n' | grep DB_DB_PASSWORD=S3cr3t # and in the process environment
Deliver secrets as files, in memory
The right pattern is a mounted file, ideally on tmpfs so it never touches disk. The app reads the file at startup; the value never enters the environment or inspect output, and a memory-backed mount vanishes when the container stops. Most official images support this directly via _FILE-suffixed variables (DB_PASSWORD_FILE) that read the value from a path. Docker secrets and the Swarm/Compose secrets mechanism implement exactly this — a read-only tmpfs file under /run/secrets.
services:app:image: myapp:1.0read_only: trueenvironment:DB_PASSWORD_FILE: /run/secrets/db_pw # app reads the FILE, not an env valuesecrets: [db_pw] # mounted at /run/secrets/db_pw (tmpfs, ro)secrets:db_pw:file: ./secrets/db_pw.txt # or external: true (from docker secret)
External stores for rotation and audit
A mounted file is a static secret; the stronger pattern keeps the source of truth outside the container entirely. A secrets manager — HashiCorp Vault is the common one — issues short-lived, automatically-rotated credentials, and a sidecar or init (Vault Agent) fetches them and writes them to a shared tmpfs the app reads. Now a leaked credential expires on its own, rotation happens in one place, and every access is audited. The container never holds a long-lived secret.
# a short-lived, auto-rotated DB credential from Vault, written to a tmpfs the app reads$ vault read database/creds/app-roleusername v-app-x7f2... # unique, expires in 1hpassword A1b2C3... # rotated automatically; revoked on lease end# the Vault Agent sidecar renders this into /run/secrets/db, shared via tmpfs