Fix Docker Compose Environment Variables Not Loading: Root Causes and Minimal Checks

Topic: docker-compose-env-variable-not-loadingUpdated 7/9/2026

Quick Answer

  • Most common root cause: Variables defined in the auto-loaded .env file are NOT automatically injected into containers—they only support ${VAR} substitution within the compose.yaml file itself. To inject variables into containers, you must use env_file: or environment: in your service definition.
  • First check: Verify whether your variable is needed for YAML substitution (use .env or --env-file) or for container runtime (use env_file: or environment:).
  • Minimal fix: Add env_file: ./app.env to your service in compose.yaml, then define KEY=VALUE pairs in app.env. For optional files, use long syntax: env_file: - path: ./app.env required: false (Compose v2.24+).
  • Version boundary: required: false for env_file requires Docker Compose v2.24+; format attribute 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:

LayerPurposeHow Variables Are Set
Compose file substitutionReplace ${VAR} in compose.yamlAuto-loaded .env file or --env-file CLI flag
Container environment injectionMake variables available inside the containerenvironment: 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:

YAML
services:
  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:

BASH
docker 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:

YAML
services:
  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:

YAML
services:
  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:

BASH
docker compose --env-file ./production.env up

-e / --env (with docker compose run)

Temporarily sets variables when running a one-off command:

BASH
docker 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:

YAML
services:
  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+):

YAML
services:
  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:

BASH
docker compose run -e VAR_NAME=value app

Or ensure the variable is exported in your shell:

BASH
export 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:

  1. Use Docker Secrets: Define secrets in compose.yaml and mount them as files inside containers (e.g., /run/secrets/db_password).
  2. Use external secret managers: HashiCorp Vault, AWS Secrets Manager, or similar.
  3. 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:

  1. Shell environment variables (highest priority)
  2. environment: in compose.yaml
  3. env_file: entries (later files override earlier ones)
  4. Dockerfile ENV instructions (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:

BASH
docker 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:

BASH
docker compose -f /path/to/compose.yaml --env-file /absolute/path/.env up

Version Compatibility

  • required: false for env_file requires Docker Compose v2.24+
  • format attribute 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:

YAML
services:
  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:

YAML
services:
  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.