| 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=3000Reference 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-abc123Key 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 -dThis 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 -dPer-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 configThis 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-keyAdd to .gitignore:
.env
.env.production
.env.staging
*.env
!.env.exampleFor 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 --environmentFAQ
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_VARThis 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.