Docker Compose check · high

Hardcoded secrets in the environment block

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.

$ explain docker-compose.yml
! db hardcodes POSTGRES_PASSWORD high
line 6
fix: 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.

Risky and safer docker-compose.yml

Riskydocker-compose.yml
services:
  db:
    image: postgres:16.4
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: s3cret-dev-pass
    restart: unless-stopped
    mem_limit: 512m
Saferdocker-compose.yml
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.

docker-compose.yml

Parsed in your browser. Nothing is uploaded.

Explanation

1 service0 networks0 volumes1 high

Top level

dbpostgres:16.4

  • Docs for image (opens in a new tab)

    Runs the image postgres:16.4.

  • Docs for environment (opens in a new tab)

    Sets 2 environment variables: POSTGRES_USER=app, POSTGRES_PASSWORD=••••••.

    The value of POSTGRES_PASSWORD is stored in the file, so anyone with repository or image access can read it.

    Safer: Read POSTGRES_PASSWORD from an untracked .env file, an interpolated variable like ${POSTGRES_PASSWORD}, or a Compose secret.

  • Docs for restart (opens in a new tab)

    Restarts if it stops or after a daemon restart (unless-stopped), but stays down if you stopped it manually.

  • Docs for mem_limit (opens in a new tab)

    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.