$docker.recipes
·11 min read·Updated January 2026

Docker Compose Environment Variables: The Complete Guide

Every method for passing environment variables to Docker Compose services — .env files, shell variables, env_file directive, and Docker secrets — with clear examples of when to use each.

docker-composeenvironment-variablesconfigurationsecrets

01Environment Variables Are More Confusing Than They Should Be

Docker Compose has at least four different ways to pass environment variables to containers, and the documentation doesn't always make it clear which one to use. I've debugged more "why isn't my env var showing up" issues than I care to admit — for myself and others. The confusion stems from a subtle distinction: there are variables that Compose itself interpolates (used in the YAML file), and variables that get passed into the running container. These are two different mechanisms, and mixing them up is the number one source of env var bugs. This guide covers every method, when to use each one, and the precedence rules that determine which value wins when the same variable is defined in multiple places.

02The .env File (Compose Interpolation)

The .env file in the same directory as your docker-compose.yml is special. Compose reads it automatically and uses its values for variable substitution in the YAML file itself. This happens before the containers start.
[.env]
1# .env (read by Compose for YAML interpolation)
2POSTGRES_VERSION=16
3DB_PASSWORD=supersecret
4APP_PORT=8080

03Using Variables in docker-compose.yml

Reference .env variables in your compose file with ${VAR} syntax:
[docker-compose.yml]
1services:
2 db:
3 image: postgres:${POSTGRES_VERSION:-16} # Default to 16
4 environment:
5 - POSTGRES_PASSWORD=${DB_PASSWORD}
6 ports:
7 - "${APP_PORT}:5432"
8
9 app:
10 image: myapp:latest
11 env_file:
12 - ./app.env # Separate env file for app-specific vars
13 environment:
14 - DATABASE_URL=postgres://user:${DB_PASSWORD}@db:5432/myapp
15 - NODE_ENV=production # Hardcoded value

Use ${VAR:-default} syntax to provide fallback values. This prevents errors when a variable isn't set and makes your compose file self-documenting.

04Variable Precedence Rules

When the same variable is defined in multiple places, Compose follows this precedence (highest to lowest): 1. Shell environment variables (exported in your terminal) 2. Values from the .env file 3. Values from env_file directive files 4. Default values in the Compose file (${VAR:-default}) This means a shell export always wins over .env, which always wins over env_file. This is useful for overriding values in CI/CD pipelines without modifying files. A common pattern I use: the .env file has development defaults, and production values are set via shell environment variables or a CI/CD system. The compose file works in both environments without changes.

05Docker Secrets for Sensitive Data

For passwords and API keys, Docker secrets are more secure than environment variables. Secrets are mounted as files inside the container rather than being visible in environment variable listings or docker inspect output:
[docker-compose.yml]
1services:
2 db:
3 image: postgres:16-alpine
4 secrets:
5 - db_password
6 environment:
7 POSTGRES_PASSWORD_FILE: /run/secrets/db_password
8
9secrets:
10 db_password:
11 file: ./secrets/db_password.txt

Always add .env files and secrets/ directories to .gitignore. Leaked credentials in Git history are the most common cause of Docker container compromises.

About the Author

Frank Pegasus

DevOps engineer and self-hosting enthusiast with over a decade of experience running containerized workloads in production. Creator of docker.recipes.