Fix Docker Compose Environment Variables Not Loading: Root Causes and Minimal Checks
Quick Answer
- Most common root cause: Variables defined in the auto-loaded
.envfile are NOT automatically injected into containers—they only support${VAR}substitution within thecompose.yamlfile itself. To inject variables into containers, you must useenv_file:orenvironment:in your service definition. - First check: Verify whether your variable is needed for YAML substitution (use
.envor--env-file) or for container runtime (useenv_file:orenvironment:). - Minimal fix: Add
env_file: ./app.envto your service incompose.yaml, then defineKEY=VALUEpairs inapp.env. For optional files, use long syntax:env_file: - path: ./app.env required: false(Compose v2.24+). - Version boundary:
required: falseforenv_filerequires Docker Compose v2.24+;formatattribute requires v2.30+.
What Problem It Solves
Docker Compose environment variable loading has a subtle but critical split in responsibility. Developers often expect that placing variables in a .env file automatically makes them available inside running containers. This is incorrect. The auto-loaded .env file only provides variable substitution for the compose.yaml file itself (e.g., ${DATABASE_URL} in YAML values). To actually inject variables into a container's runtime environment, you must explicitly use the environment: or env_file: directives.
This confusion leads to containers that start but immediately exit with "Environment variable not set" errors, or services that run with default values instead of configured ones.
Root Cause Analysis
The Docker Compose environment variable system has two distinct layers:
| Layer | Purpose | How Variables Are Set |
|---|---|---|
| Compose file substitution | Replace ${VAR} in compose.yaml | Auto-loaded .env file or --env-file CLI flag |
| Container environment injection | Make variables available inside the container | environment: or env_file: in service definition |
The .env file (auto-loaded from the same directory as compose.yaml) only serves the first layer. Variables defined there are not passed to containers unless you also list them under environment: or reference the file via env_file:.
Minimal Working Configuration
Create two files in your project directory:
compose.yaml:
YAMLservices: app: image: alpine:latest command: sh -c "echo DATABASE_URL=\$DATABASE_URL && echo API_KEY=\$API_KEY" env_file: ./app.env
app.env:
DATABASE_URL=postgres://user:pass@db:5432/mydb
API_KEY=sk-abc123
Run with:
BASHdocker compose up
The container will print the injected variables. Without env_file: ./app.env, those variables would not appear inside the container even if they existed in the auto-loaded .env file.
Parameters and Environment Variables
environment: (inline)
Sets variables directly in compose.yaml. Supports two syntaxes:
YAMLservices: app: environment: - NODE_ENV=production - DEBUG=true # or mapping syntax: environment: NODE_ENV: production DEBUG: "true"
env_file: (external file)
Specifies one or more files to load into the container environment:
YAMLservices: app: env_file: ./app.env # Multiple files (later files override earlier ones): env_file: - ./common.env - ./override.env # Optional file (Compose v2.24+): env_file: - path: ./optional.env required: false
.env (auto-loaded)
Automatically loaded from the same directory as compose.yaml. Used for ${VAR} substitution in the YAML file only. Not injected into containers.
--env-file (CLI)
Overrides the auto-loaded .env file for YAML substitution:
BASHdocker compose --env-file ./production.env up
-e / --env (with docker compose run)
Temporarily sets variables when running a one-off command:
BASHdocker compose run -e MY_VAR=value app docker compose run -e MY_VAR app # takes value from shell or .env
Common Errors and Fixes
Error: WARN[0000] The "FOO" variable is not set. Defaulting to a blank string.
Cause: ${FOO} in compose.yaml is not defined in the auto-loaded .env file, --env-file, or shell environment.
Fix: Ensure the variable exists in the appropriate file. For optional variables, use default values in YAML:
YAMLservices: app: image: myapp environment: - PORT=${PORT:-8080}
Error: Error response from daemon: ... env_file: ./app.env: no such file or directory
Cause: The path specified in env_file: does not exist relative to compose.yaml.
Fix: Verify the file path. For optional files, use the long syntax (Compose v2.24+):
YAMLservices: app: env_file: - path: ./app.env required: false
Error: Container exits immediately with "Environment variable not set"
Cause: The application expects a variable that was placed only in the auto-loaded .env file (YAML substitution) but not injected into the container.
Fix: Add env_file: or environment: to the service definition. The auto-loaded .env file does not inject variables into containers.
Error: docker compose run -e VAR_NAME does not pass the variable
Cause: docker compose run -e VAR_NAME reads from the current shell environment or .env file. If neither has the variable, nothing is passed.
Fix: Explicitly set the value:
BASHdocker compose run -e VAR_NAME=value app
Or ensure the variable is exported in your shell:
BASHexport VAR_NAME=value docker compose run -e VAR_NAME app
Production Notes and Security Checks
Sensitive Information
Environment variables in environment: and env_file: are stored in plaintext in compose.yaml and .env files. Do not use them for passwords, tokens, or API keys in production. Instead:
- Use Docker Secrets: Define secrets in
compose.yamland mount them as files inside containers (e.g.,/run/secrets/db_password). - Use external secret managers: HashiCorp Vault, AWS Secrets Manager, or similar.
- If you must use env files: Set file permissions to
600, add to.gitignore, and inject via CI/CD secure variables.
Variable Override Order
Understanding precedence prevents unexpected behavior:
- Shell environment variables (highest priority)
environment:incompose.yamlenv_file:entries (later files override earlier ones)- Dockerfile
ENVinstructions (lowest priority)
Note: environment: values override env_file: values but do not override shell environment variables.
File Locking and Concurrency
Multiple docker compose instances operating on the same project directory can cause inconsistent reads of .env files. In CI/CD pipelines, ensure serial execution or use unique project names:
BASHdocker compose -p my-project-${CI_JOB_ID} up
Path Resolution
env_file: paths are relative to compose.yaml. In complex directory structures or CI environments, use absolute paths or ensure the working directory is correct:
BASHdocker compose -f /path/to/compose.yaml --env-file /absolute/path/.env up
Version Compatibility
required: falseforenv_filerequires Docker Compose v2.24+formatattribute requires v2.30+- Older versions will silently ignore or error on these attributes
FAQ
Q: Why can't my container see variables I defined in the .env file?
A: The auto-loaded .env file only supports ${VAR} substitution in compose.yaml. It does not inject variables into containers. To make variables available inside containers, use env_file: (pointing to a KEY=VALUE file) or environment: in your service definition. For example, add env_file: ./app.env to your service, then define variables in app.env.
Q: How do I share the same environment variables across multiple services?
A: Use env_file: with the same file in multiple services:
YAMLservices: web: env_file: ./common.env worker: env_file: ./common.env
Variables from common.env are injected into both services. To override specific variables for a service, add environment: entries with the same name—they take precedence over env_file:.
Q: How should I handle sensitive variables like database passwords in production?
A: Docker recommends using secrets instead of environment variables for sensitive data. In compose.yaml:
YAMLservices: app: secrets: - db_password secrets: db_password: file: ./db_password.txt
The application reads the secret from /run/secrets/db_password. If you must use environment variables, ensure .env files have 600 permissions, are excluded from version control, and are injected via CI/CD secure variables.
Official References
Related Guides
Related English guides will appear here as the /en knowledge graph grows.