| Method | Syntax | Scope |
|---|---|---|
| Inline in YAML | environment: - KEY=value | Single service |
| .env file (auto-loaded) | Create .env next to docker-compose.yml | Variable substitution in YAML |
| env_file directive | env_file: - ./app.env | Injected into container |
| --env-file CLI flag | docker compose --env-file .env.prod up | Variable substitution in YAML |
Inline Environment Variables
The simplest way to set environment variables in Docker Compose is directly in your docker-compose.yml:
services:
app:
image: node:20
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=postgres://db:5432/myappYou can also use the map syntax (without the dash):
services:
app:
image: node:20
environment:
NODE_ENV: production
PORT: "3000"Both formats are equivalent. The list syntax (- KEY=value) is more common in the wild.
Using .env Files with Docker Compose
Docker Compose automatically loads a file named .env in the same directory as your docker-compose.yml. Variables from this file are available for substitution in the YAML file itself — they are NOT automatically injected into containers.
Create a .env file:
# .env POSTGRES_VERSION=16 APP_PORT=3000
Reference these variables in docker-compose.yml with ${} syntax:
services:
db:
image: postgres:${POSTGRES_VERSION}
ports:
- "${APP_PORT}:5432"Important distinction: The .env file is for Compose file interpolation. To inject variables into the container's environment, use the env_file directive (next section).
The env_file Directive
To inject environment variables directly into a container, use env_file:
services:
app:
image: node:20
env_file:
- ./app.env
- ./secrets.envWhere app.env contains:
# app.env NODE_ENV=production PORT=3000 API_KEY=sk-abc123
Key behaviors:
- Variables are injected into the container's environment (visible via
docker exec <container> env) - Later files override earlier ones if the same variable appears
- Lines starting with
#are comments - Empty lines are ignored
- No quotes needed around values (quotes become part of the value)
The --env-file CLI Flag
The --env-file flag replaces the default .env file for Compose file interpolation:
# Use .env.prod instead of .env for variable substitution docker compose --env-file .env.prod up # Combine with other flags docker compose --env-file .env.staging up -d
This flag affects which file is used for ${} substitution in docker-compose.yml — it does NOT change what gets injected into containers via env_file.
Multiple .env Files for Different Environments
A common pattern is maintaining separate env files per environment:
project/ ├── docker-compose.yml ├── .env # Default/development values (auto-loaded) ├── .env.staging # Staging overrides ├── .env.production # Production overrides ├── app.env # App-specific vars (injected into container) └── db.env # Database vars (injected into container)
# docker-compose.yml
services:
app:
image: myapp:latest
env_file:
- ./app.env
environment:
- APP_ENV=${APP_ENV:-development}
db:
image: postgres:${POSTGRES_VERSION:-16}
env_file:
- ./db.envRun with different environments:
# Development (uses .env automatically) docker compose up # Staging docker compose --env-file .env.staging up # Production docker compose --env-file .env.production up -d
Per-Service Environment Configuration
Each service can have its own combination of inline variables and env files:
services:
frontend:
image: nginx:alpine
environment:
- NGINX_PORT=80
backend:
image: node:20
env_file:
- ./backend.env
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://db:5432/app
db:
image: postgres:16
env_file:
- ./db.env
environment:
- POSTGRES_DB=myappPriority order (highest wins):
environmentvalues indocker-compose.yml- Shell environment variables on the host
env_filevalues- Dockerfile
ENVdefaults
Variable Substitution and Interpolation
Docker Compose supports shell-like variable substitution in YAML:
services:
app:
image: myapp:${TAG:-latest}
ports:
- "${PORT:?PORT must be set}:3000"
environment:
- DEBUG=${DEBUG:-false}| Syntax | Behavior |
|---|---|
| ${VAR} | Value of VAR, empty if unset |
| ${VAR:-default} | Default if VAR is unset or empty |
| ${VAR-default} | Default only if VAR is unset |
| ${VAR:?error} | Error message if VAR is unset or empty |
| ${VAR?error} | Error message only if VAR is unset |
Check what Compose will resolve:
docker compose config
This prints the fully resolved docker-compose.yml with all variables substituted.
Secrets Management Best Practices
Never commit secrets to version control. Here's a safe pattern:
# .env.example (commit this — documents required variables) DATABASE_URL= API_KEY= JWT_SECRET= # .env (DO NOT commit — add to .gitignore) DATABASE_URL=postgres://user:pass@db:5432/app API_KEY=sk-live-abc123 JWT_SECRET=super-secret-key
Add to .gitignore:
.env .env.production .env.staging *.env !.env.example
For production, consider Docker secrets:
services:
app:
image: myapp:latest
secrets:
- db_password
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txtCommon Pitfalls and Debugging
# WRONG — value will be "production" (with quotes) NODE_ENV="production" # RIGHT — value will be production NODE_ENV=production
.env(auto-loaded) → substitution indocker-compose.ymlonlyenv_filedirective → injected into container environment
# Recreate containers to pick up env changes docker compose up -d --force-recreate # Or rebuild if using build-time args docker compose up -d --build
# See resolved compose file docker compose config # See container environment docker exec <container> env # See what .env file is loaded docker compose config --environment
FAQ
Can I use multiple .env files at once?
Not with the auto-loaded .env. Docker Compose only auto-loads one .env file. Use --env-file to specify a different one, or use the env_file directive in YAML for container-level env files (which does support multiple files).
What's the difference between environment and env_file?
environment sets variables inline in the YAML. env_file loads variables from a file. Both inject into the container. environment values take priority over env_file values.
Do I need docker-compose.yml or compose.yml?
Both work. Docker Compose V2 (the docker compose command) prefers compose.yml but supports both. Use whichever your team prefers.
How do I pass host environment variables to a container?
List the variable name without a value:
environment: - MY_HOST_VAR
This passes the host's MY_HOST_VAR value into the container.
Does docker compose --env-file affect env_file in the YAML?
No. --env-file only affects ${} substitution in the Compose file. The env_file directive in YAML independently loads its specified files into the container.