Docker

Docker Environment Variables Not Loading From .env File

When environment variables from a .env file don't show up inside a container, the cause is usually a mismatch between what actually reads that file (Docker Compose) and what doesn't (a plain docker build or docker run), or a formatting issue in the file itself.

The Problem

You define variables in a .env file expecting them to be available inside the container, but your application reports them as missing or undefined:

console.log(process.env.API_KEY); // undefined
echo $API_KEY  # empty

The file clearly exists in the project directory with the expected content, which makes the missing values confusing.

Why It Happens

Whether a .env file is automatically loaded depends entirely on how you're running the container, and this behavior surprises a lot of people:

  • A plain docker run command does not automatically read any .env file — Docker Compose does this automatically, but the base Docker CLI does not, so a working Compose setup and a manual docker run command behave completely differently for the same project
  • Docker Compose's automatic .env loading only substitutes variables into the docker-compose.yml file itself (for things like image tags or port mappings) — it does not automatically pass those same variables into the container's runtime environment unless explicitly configured to with env_file or environment
  • Syntax issues in the .env file itself, like quotes around values that get included literally, or spaces around the equals sign, which some parsers don't handle the way you'd expect
  • The .env file is in the wrong location — Compose looks for it relative to the directory the docker compose command is run from, not necessarily the same directory as the Dockerfile

The Fix

If you're using Docker Compose and want variables from .env to actually be available inside the container (not just substituted into the compose file), explicitly reference the file with env_file:

services:
  app:
    build: .
    env_file:
      - .env

This is the key distinction: Compose reading .env for variable substitution in the YAML file itself happens automatically, but passing those variables into the running container requires this explicit env_file directive (or listing them individually under environment).

If you're running with plain docker run rather than Compose, pass the file explicitly, since there's no automatic loading at all in this case:

docker run --env-file .env your-image

Check your .env file's syntax — avoid quotes around values unless you specifically want them included as literal characters, and avoid spaces around the equals sign:

# Correct
API_KEY=abc123

# Usually wrong - quotes become part of the value in some parsers
API_KEY="abc123"

# Wrong - spaces around = often break parsing entirely
API_KEY = abc123

Confirm the file's actual location matches where the command expects it. For Docker Compose, this is the directory you run docker compose from, which isn't necessarily where your Dockerfile lives if your project has a more complex folder structure:

docker compose --env-file ./config/.env up

Use this explicit flag if your .env file isn't in the default expected location.

Still Not Working?

If variables are confirmed present with env_file configured correctly, but a specific variable still doesn't appear inside the container, check whether it's being overridden by a conflicting ENV instruction baked into the Dockerfile itself, or by an environment: block in the compose file listing the same variable with a different (possibly empty or stale) value — later declarations in the configuration chain generally take precedence, so a hardcoded value elsewhere can silently override what you set in the .env file.

It's also worth checking whether the variable is defined correctly but simply isn't being read by your application code the way you expect. Confirm what the container actually sees, independent of your application's own parsing logic:

docker exec your-container env | grep API_KEY

If this shows the variable correctly, but your application still reports it as missing, the issue has shifted from Docker configuration to how your application code reads environment variables — for example, some frameworks require their own separate .env loading library (like dotenv in Node.js) to populate process.env, and that library's own configuration might be looking for the file in the wrong location relative to where the application actually runs inside the container.

Finally, if you're using Docker secrets or a secrets management tool alongside plain environment variables, confirm you're not accidentally expecting a secret-based variable to behave like a regular one — secrets are typically mounted as files rather than environment variables by default, requiring explicit code changes to read them differently than a standard process.env lookup would.