Environment Variables
Containers get their configuration from environment variables — database passwords, ports, feature flags. Compose gives you three ways to set them, and the differences matter.
1. environment: — right in the file
services:
web:
image: nginx:alpine
environment:
APP_ENV: production
DEBUG: "false"
Fine for non-secret settings. Two forms exist — the map
above, and a list (- APP_ENV=production). The map is
easier to read; use it.
Do not put secrets here if the file goes into git (which it should). Passwords and API keys committed to a repo are compromised the moment they're pushed — even to a private repo you later make public, even in history after you "remove" them.
2. env_file: — a separate file per service
services:
web:
image: nginx:alpine
env_file:
- app.env
app.env is plain KEY=value lines:
LOG_LEVEL=info
WORKERS=2
Use env_file for non-secret service settings you want
out of the compose file. For actual secrets, the file must
be git-ignored (add it to .gitignore) and created on
each machine — or better, see option 3.
3. .env + interpolation — the flexible one
A file named exactly .env, sitting next to
compose.yaml, is loaded automatically. Its values can
be interpolated into the compose file with ${VAR}:
services:
web:
image: nginx:alpine
ports:
- "${WEB_PORT:-8080}:80"
environment:
APP_ENV: ${APP_ENV}
DB_PASSWORD: ${DB_PASSWORD:?a value is required here}
With .env containing:
WEB_PORT=8080
APP_ENV=production
Rendered (verified with Compose v5.6.0):
environment:
APP_ENV: production
DEBUG: "false"
LOG_LEVEL: info
WORKERS: "2"
Three interpolation tricks:
${VAR:-default}— usedefaultwhen unset. Ports and other "usually this" values.${VAR:?message}— fail fast withmessagewhen unset. Use for secrets:error while interpolating services.web.environment.DB_PASSWORD: a value is required herebeats a container crashing at 2 AM because the password was empty.- Plain
${VAR}— unset means Compose warns (The "APP_MODE" variable is not set. Defaulting to a blank string.) and substitutes empty. Fine for optional things, dangerous for required ones.
Precedence: who wins
When the same variable is set in several places:
docker compose run -e KEY=...(command line)- Shell environment (
export KEY=...beforeup) environment:in the compose fileenv_file:.envfile
So the shell beats .env — handy for secrets: keep
DB_PASSWORD out of every file and export it (or use
your shell's env management) before up.
The secrets rule
Short version: compose files are documentation; don't
document your passwords. Passwords and keys arrive via
the shell, a git-ignored file, or a secrets manager —
never hardcoded under environment: in a committed file.