Docker Compose Override File Not Applying Environment Variables
When variables in a docker-compose.override.yml file don't seem to take effect, the cause is usually either the file not being picked up automatically due to a naming or location mismatch, or a misunderstanding of how Compose merges override values with the base file rather than fully replacing them.
The Problem
You define environment variables in an override file, expecting them to apply on top of (or instead of) what's in your main docker-compose.yml, but the container still shows the original values:
docker exec your-container env | grep API_URL
API_URL=http://old-value.internal
# expected the override file's new value instead
Why It Happens
Docker Compose's override mechanism has specific rules about file naming, discovery, and how values actually merge — deviating from any of these silently causes the override to be ignored rather than raising an error. Common causes:
- The file isn't named exactly
docker-compose.override.yml, which is the specific filename Compose automatically detects and merges without needing any explicit flag — a differently named file (likedocker-compose.override.yamlwith a different extension, or a custom name) requires being explicitly specified, or it's silently ignored - The override file isn't in the same directory as the main
docker-compose.yml, so Compose's automatic discovery doesn't find it - The service name in the override file doesn't exactly match the service name in the base file, so Compose treats it as defining a new, separate service rather than merging into the existing one
- A misunderstanding of merge behavior for specific keys — some Compose configuration keys merge (adding to what's already there), while others fully replace the base value, and environment variables specifically follow a merge-by-key behavior that can produce unexpected results if not understood correctly
The Fix
First, confirm the exact filename and location. For automatic discovery without any extra flags, the file must be named precisely:
docker-compose.override.yml
Located in the same directory as your primary docker-compose.yml. If you're intentionally using a different filename (for managing multiple environment-specific overrides, for example), you need to explicitly reference both files together:
docker compose -f docker-compose.yml -f docker-compose.staging.yml up
Confirm the service name in the override file matches the base file exactly:
# docker-compose.yml
services:
app:
environment:
API_URL: http://old-value.internal
# docker-compose.override.yml - service name "app" must match exactly
services:
app:
environment:
API_URL: http://new-value.internal
Verify what Compose actually resolves the final merged configuration to, rather than assuming based on reading both files separately:
docker compose config
This command outputs the fully merged configuration Compose will actually use, combining base and override files according to its real merge rules — reviewing this output directly removes any guesswork about whether your override is actually being applied and what the final value is.
Be aware that for the environment key specifically, Compose merges at the level of individual variable names — a variable defined in both files gets the override file's value, but variables that only exist in the base file remain present unless the override doesn't touch them, and this behaves differently from lists (like ports or volumes), which typically get replaced entirely rather than merged, depending on the specific key and Compose version.
If you changed the override file but the container is still running with old values, remember that environment variable changes require recreating the container, not just restarting it — a plain restart reuses the existing container's already-set environment:
docker compose up -d --force-recreate
Still Not Working?
If docker compose config shows the correct merged value but the running container still shows the old one, confirm you're not accidentally running an older container that was never actually recreated, by checking its creation timestamp against when you made the change:
docker inspect your-container --format='{{.Created}}'
If this timestamp predates your configuration change, the container genuinely hasn't been recreated with the new configuration yet, regardless of what the compose file itself now says — running docker compose up -d --force-recreate (or explicitly stopping and removing the old container before bringing it back up) resolves this specific gap between configuration and actual running state.