For delivering a database password to a running container, compare an environment variable set with `docker run -e DB_PASSWORD=...` against a file mounted into the container. What leakage paths does each have, and which would you choose?
answer
- env: inspect, inherited, /proc/pid/environ, crash dumps
- file: mode + owner, not in inspect, rotate in place
- tmpfs so it never hits disk
- DB_PASSWORD_FILE convention, /run/secrets/<name>
- stopped containers still hold their config
basics
~20 sEnvironment values show up in docker inspect, are inherited by every child process, are readable in /proc/<pid>/environ, and get captured by crash and error reporters. A file can be permission-scoped, stays out of inspect output, and can be rotated in place. Prefer a mounted file, ideally on tmpfs.
solid answer
~50 s**Environment variables** are convenient and universally supported, but they are broadcast: the value is stored in the container config, so `docker inspect` reveals it to anyone with daemon access; every child process inherits it; `/proc/<pid>/environ` exposes it to same-user processes; crash handlers, APM agents and error trackers routinely ship the environment to a third-party system; and it appears in shell history and CI job definitions. Rotation means recreating the container. **Mounted files** are narrower: `docker inspect` shows only the mount path, permissions and ownership limit which uid can read it, the app can re-read after rotation without a restart, and the material is not inherited by subprocesses. Costs: it exists on the host filesystem, so it can be backed up or read by host users unless you place it on tmpfs and set the mode. I choose the file, exposing the *path* through an environment variable — the widely used `DB_PASSWORD_FILE` convention, matching `/run/secrets/<name>`.
code
bash · 12 linesdocker run -d --name a -e DB_PASSWORD=s3cr3t app:1.0
docker inspect -f '{{json .Config.Env}}' a
# ["PATH=...","DB_PASSWORD=s3cr3t"]
docker exec a cat /proc/1/environ | tr '\0' '\n' | grep DB_
# file-based, in memory, path-only in the environment
docker run -d --name b \
--tmpfs /run/secrets:rw,noexec,nosuid,mode=0700,size=1m \
-v /etc/creds/db:/run/secrets/db_password:ro \
-e DB_PASSWORD_FILE=/run/secrets/db_password app:1.0
docker inspect -f '{{json .Config.Env}}' b
# ["PATH=...","DB_PASSWORD_FILE=/run/secrets/db_password"]go deeper
Know that environment variables are visible via docker inspect and inherited by child processes, and that a mounted file is the safer default.
Enumerate the concrete paths — inspect, /proc/<pid>/environ, inheritance — and describe the *_FILE convention with /run/secrets.
Weigh rotation, tmpfs placement, permissions and telemetry capture, and offer the entrypoint wrapper for apps that only accept env vars.
Set the org convention and its enforcement, define rotation SLAs, and decide who may call docker inspect on hosts running production workloads.
## Why this question is asked Both approaches "work", so the interviewer is testing whether you can enumerate real disclosure paths rather than repeat twelve-factor slogans. ## Leakage paths of environment variables - **Container config.** The value is stored in the container's JSON, so `docker inspect` prints it. On a shared host or CI runner, everyone with daemon access reads every service's credentials — including from *stopped* containers still on disk. - **Inheritance.** The environment is copied to every child process by default. A shell-out, a build tool, a debugging command — all carry the credential. - **/proc.** `/proc/<pid>/environ` is readable by the same uid (and by root), so any code running as the app user can read it, even code that had no business receiving it. - **Telemetry and crash paths.** Error trackers, core dumps, heap dumps and diagnostic endpoints commonly serialize the environment. This is the most common real-world disclosure: the credential ends up in a third-party SaaS. - **Upstream artifacts.** `--env-file` contents, shell history, CI job definitions and orchestrator objects all persist the plaintext value somewhere. - **Rotation.** Changing the value requires recreating the container, so rotation becomes a deployment. ## Leakage paths of mounted files - **Host storage.** The file exists on the host; backups, snapshots or other host users may capture it. Mitigate by placing it on **tmpfs** so it is memory-backed and never written to disk, and by setting restrictive mode and ownership. - **Container access.** Anyone able to run `docker cp` or exec into the container can read it — but that is true of the environment as well, and worse. - **Sloppy modes.** A world-readable bind mount defeats the point; the file should be readable only by the uid the process runs as. - **Application handling.** If the app reads the file and then logs it or re-exports it as an env var for a child process, you are back where you started. The decisive advantages of files: they are not in `inspect` output, they are not inherited, they carry filesystem permissions, and they can be replaced in place so a process that re-reads on demand picks up a rotated value without a restart. ## The practical convention Most official images support a `*_FILE` variant: instead of `POSTGRES_PASSWORD` you set `POSTGRES_PASSWORD_FILE=/run/secrets/db_password`. The environment carries only a **path** — harmless in `inspect` — while the value lives in a mode-0400 file on tmpfs. Docker Compose and Swarm `secrets:` entries deliver exactly that shape, mounting each secret at `/run/secrets/<name>`. If you own the application, implement the same convention: check `VAR_FILE` first, fall back to `VAR`, and read the file at startup or on a signal. ## When environment variables are acceptable When the platform gives you nothing better, when the value is low-sensitivity, or when the runtime injects it per-process from a secret manager so it never persists in a config object. Be pragmatic in the interview: state that env vars are the default because they are universal, then explain the specific disclosure paths that make a file better for real credentials, and note that neither approach protects against an attacker who already has code execution as the app user — they narrow exposure, they do not eliminate it. ## What to add if pressed Mention rotation strategy (file replace plus SIGHUP or periodic re-read), keeping material out of images entirely, restricting who can call `docker inspect` at all, and scrubbing the environment from crash reporters as a compensating control where env vars cannot be avoided.
- Your application only supports an environment variable for the password. How do you reduce the risk without changing the app?Wrap the process with an entrypoint that reads the file and execs the app with the variable set for that process only, so it never appears in the container config or in docker inspect. Combine that with scrubbing the environment from crash reporters and APM agents, and restricting who can reach the daemon. It is a mitigation, not a fix: child processes still inherit the value.
- How does the file approach make credential rotation easier?The file can be replaced in place on the mount, so a process that re-reads on demand or on SIGHUP picks up the new value without a restart. With an environment variable the value is fixed at process creation, so rotation means recreating the container and taking a deployment. That difference is what makes short rotation intervals practical.
saying these in an interview costs you the question
- Claiming env vars are safe because 'they only exist inside the container'
- Forgetting docker inspect exposes Config.Env, including for stopped containers
- Mounting a secret file world-readable, or leaving it on a backed-up host path instead of tmpfs
- Ignoring that crash reporters and APM agents commonly ship the whole environment off-host
- Treating either option as protection against an attacker who already runs code as the app user