A literal value for a variable named like a secret (PASSWORD, TOKEN, SECRET, API_KEY, names ending in _KEY and similar) in a compose file is stored wherever the file goes: the repository, its history, forks and CI logs. The explainer flags these as high. Interpolated values such as ${DB_PASSWORD} are not flagged, because the value then lives outside the file.
openrouting — compose explainer
$ explain docker-compose.yml! db hardcodes POSTGRES_PASSWORD highline 6fix: see below$
What the free Docker Compose explainer reports on the risky example below.
Why it matters
Compose files are committed and shared. A password written in one is readable by everyone with access to the repository, and it stays in the version history after it is removed from the latest commit.
Development values tend to reach production. A password that was only meant for a laptop often ends up being the one the deployed database uses.
Rotating a hardcoded secret means changing the file and every copy of it, so it tends not to happen.
services:
db:
image: postgres:16.4
environment:
POSTGRES_USER: app
# Read from an untracked .env file or the shell; fails fast if missing
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
restart: unless-stopped
mem_limit: 512m
How to fix it
Replace the literal with an interpolated variable, e.g. ${POSTGRES_PASSWORD:?}, and set it in a .env file listed in .gitignore or in the deployment environment.
For values that should not appear in the container's environment at all, use Compose secrets, which mount the value as a file under /run/secrets/. Many official images read it through a _FILE variable, e.g. POSTGRES_PASSWORD_FILE: /run/secrets/db_password.
Treat any secret that was committed as exposed: rotate it, then remove it from the file.
When it's acceptable
Throwaway credentials for a local-only service that never holds real data and is never reachable from other machines. Even then, interpolation costs little and avoids the habit.
Check your own docker-compose.yml
The risky example is loaded below. Paste your own file to check it for this and every other Docker Compose rule. It runs in your browser; nothing is uploaded.
Limits memory to 512m. The container is killed if it exceeds this.
Questions
Is it safe to put passwords in docker-compose.yml?
No. The file is usually committed, so the password is readable by anyone with repository access and stays in its history. Interpolate it from an untracked .env file or use Compose secrets.
Does using a .env file keep secrets out of the container?
No, it keeps them out of the compose file. The value still becomes an environment variable in the container and is visible to anyone who can inspect it. Compose secrets avoid that by mounting the value as a file.
Which variable names does the explainer treat as secrets?
Names containing PASSWORD, PASSWD, SECRET, TOKEN, API_KEY, PRIVATE_KEY, ACCESS_KEY or CREDENTIAL, names ending in _KEY, and short forms such as DB_PASS or MYSQL_PWD. It only checks the compose file itself, not .env files.
Does the explainer flag POSTGRES_PASSWORD_FILE?
No. A variable ending in _FILE whose value is a file path, such as POSTGRES_PASSWORD_FILE: /run/secrets/db_password, points at a secret instead of containing one, so it is not flagged. Any other value under a _FILE name still is.