10 Lines to Better Docker Compose Secrets

This is a pracยญtiยญcal patยญtern I use when conยญtainerยญized apps expect enviยญronยญment variยญables but I want the secuยญriยญty benยญeยญfits of file-โ€‹mounted secrets. Drop the shell script below next to your Docker Compose files and you can do the same.

Quick overview

Secrets like passยญwords and API keys belong outยญside your reposยญiยญtoยญry and appliยญcaยญtion image layยญers. Docker Compose can mount such secrets in your conยญtainยญers as files under /run/secrets, which keeps them out of images and verยญsion conยญtrol. But many apps still expect conยญfigยญuยญraยญtion via enviยญronยญment variยญables. Rather than changยญing app code, I use a tiny wrapยญper script that:

  • reads every file in /run/secrets
  • exports each fileโ€™s conยญtents as an enviยญronยญment variable
  • then execs the origยญiยญnal command

Itโ€™s small, preยญdictable, portable, and keeps secrets from mixยญing with your verยญsioned .env enviยญronยญment files and out of your Compose files.

How it works

  • Location: Docker Compose mounts secrets into the conยญtainยญer at /run/secrets/<NAME>.
  • Mapping rule: The wrapยญper uses those file names as enviยญronยญment variยญable names; the file conยญtents become the valยญues. Secret names in your Compose file must be valid shell idenยญtiยญfiers (they become both the file names in /run/secrets and the exportยญed variยญable names).
  • Execution: After exportยญing variยญables, the script uses exec "$@" so that the wrapped process replaces the shell and inherยญits the exportยญed environment.
  • Security modยญel: Secrets remain files you can perยญmisยญsion approยญpriยญateยญly on the host; theyโ€™re not baked into images or stored in your Compose YAML as plain text.

The script

Letโ€™s call it with-secrets.sh:

#!/bin/sh
set -eu

for secret_file in /run/secrets/*; do
  [ -e "$secret_file" ] || continue
  if [ -f "$secret_file" ]; then
    name=$(basename "$secret_file")
    export "$name=$(cat "$secret_file")"
  fi
done

exec "$@"

Notes about the script

  • set -eu fails fast on unset variยญables or errors.
  • Since it exports each secret using the file name as the variยญable name, sanยญiยญtize the file name if you need difยญferยญent enviยญronยญment variยญable names.
  • The final exec hands conยญtrol to your app withยญout leavยญing an extra shell process.

Example Compose snippet

services:
  app:
    image: your-app:latest
    secrets:
      - DB_PASS
      - API_KEY
    volumes:
      - ./with-secrets.sh:/with-secrets.sh:ro
    command: ["/with-secrets.sh", "your-original-command", "--with-args"]

secrets:
  DB_PASS:
    file: ./secrets/db_password.txt
  API_KEY:
    file: ./secrets/api_key.txt

Behavior: DB_PASS and API_KEY above appear as files (/run/secrets/DB_PASS, /run/secrets/API_KEY); the mountยญed with-secrets.sh wrapยญper script exports them as DB_PASS and API_KEY enviยญronยญment variยญables for your-original-command --with-args.

Decision points and alternatives

  • Prefer native *_FILE supยญport if your app supยญports it (e.g., PostgreSQLโ€™s PGPASSFILE). That avoids the wrapยญper entirely.
  • For multi-โ€‹host or high-โ€‹compliance deployยญments, use an exterยญnal secrets manยญagยญer (e.g., Hashicorp Vault, cloud KMS, SOPS) rather than Compose secrets.
  • Build-โ€‹time secrets are a sepยญaยญrate conยญcern; use BuildKit or dedยญiยญcatยญed build secret mechยญaยญnisms to avoid leakยญing creยญdenยญtials into your image layers.

Risks and mitigations

  • Risk: Accidentally logยญging or dumpยญing enviยญronยญment variยญables
    Mitigation: Never print enviยญronยญment variยญables in logs and restrict debug output
  • Risk: Secret file names that are not valid shell idenยญtiยญfiers
    Mitigation: Normalize or map file names to safe enviยญronยญment variยญable names before exporting
  • Risk: Secrets checked into git or othยญer verยญsion conยญtrol
    Mitigation: Keep secret files out of repos, add strict .gitignore rules, and inject secrets via CI/โ€‹CD or runยญtime provisioning

Final notes

This patยญtern is intenยญtionยญalยญly pragยญmatยญic: it preยญserves the secuยญriยญty advanยญtage of file-โ€‹mounted secrets while letยญting unmodยญiยญfied apps keep using enviยญronยญment variยญables. Itโ€™s not a silยญver bulยญlet for every environmentโ€“use it where Compose secrets are approยญpriยญate and pair it with stronger secret stores for production-โ€‹grade, multi-โ€‹host deployments.


Discover more from The Phoenix Trap

Subscribe to get the latest posts sent to your email.

Mark Gardner Avatar

Hi, Iโ€™m Mark.

Hi, Iโ€™m Mark Gardยญner, and this is my personal blog. I show software developers how to level up by building production-ready things that work. Clear code, real projects, lessons learned.