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 runcommand does not automatically read any.envfile — Docker Compose does this automatically, but the base Docker CLI does not, so a working Compose setup and a manualdocker runcommand behave completely differently for the same project - Docker Compose's automatic
.envloading only substitutes variables into thedocker-compose.ymlfile 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 withenv_fileorenvironment - Syntax issues in the
.envfile 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
.envfile is in the wrong location — Compose looks for it relative to the directory thedocker composecommand 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.