Infrastructure
Docker Compose Environment Variables: Trace the Value
Trace Docker Compose values through interpolation and container precedence, inspect resolved configuration safely and prevent incorrect deployment settings.
In this article
Docker Compose Environment Variables: Trace the Value
A Docker Compose variable can have one value in your terminal and another inside the container. The confusion usually comes from treating interpolation of the Compose file and assignment of the container environment as the same operation. Trace those stages separately before changing credentials or rebuilding images.
This guide focuses on provenance: where did the final value come from, and which configuration path overrode the one you expected? Use synthetic values for the investigation, because rendering configuration can reveal secrets that should not appear in shared logs.
Distinguish interpolation from injection
Interpolation substitutes values into the Compose model. Container environment settings define what the launched process receives. A project .env file used for interpolation does not mean every variable in that file is automatically passed to every container.
The Docker interpolation guide documents variable substitution, defaults and inspection with docker compose config. The final environment also depends on how variables are declared in the service and on the relevant precedence rules.
Begin with a small service configuration:
services:
app:
image: example/app:1.0
environment:
APP_MODE: ${APP_MODE:-development}This demonstrates a default during interpolation. It doesn't say where every other process variable will come from.
Build a provenance table
For each problematic setting, list candidate sources: invoking shell, interpolation file, command-line environment file, service environment, service env_file, image defaults and runtime overrides. Add the observed value at each stage.
Use distinctive synthetic values such as from-shell and from-service-file. That is safer and clearer than inspecting a real database password. Run the experiment in a test project so a changed value cannot affect a production service.
Document the command's working directory and the exact Compose files selected. Invoking the same command from a different location or with a different -f argument can select a different project context.
Inspect the resolved model
Run docker compose config against the intended test configuration. Review the resolved service environment and image reference. Compare it with the unrendered source to see which substitutions actually occurred.
Be careful: resolved output can contain secrets. Don't paste the complete production output into chat or a public issue. Create a sanitized copy that preserves field names and provenance labels while removing sensitive values.
Use YAML Validator to inspect sanitized syntax and Text Diff to compare two safe rendered outputs. The tools cannot reproduce Compose's environment selection merely by reading a dotenv file.
Inspect what the process receives
Once the rendered model is correct, verify the container's effective configuration through a safe application diagnostic. Prefer reporting selected nonsecret settings rather than printing the entire environment.
If you inspect a container shell, remember that the main process may load another application configuration file after startup. A correct container environment does not prove the application used the value. Check the framework's configuration cache or startup loading path.
After a change, confirm whether the container was recreated and whether the application restarted. Editing a file on disk doesn't automatically update an already running process's environment. Record the deployment action rather than assuming a restart occurred.
Make required values fail clearly
Use explicit required-value handling where a missing setting would create a dangerous or unusable deployment. Docker's substitution syntax supports required-value forms; choose the one that matches your distinction between unset and empty values.
Don't default a production database location to an unrelated local service merely to make rendering succeed. A clear deployment failure is easier to diagnose than a healthy-looking application connected to the wrong database.
For optional settings, document the default and its operational impact. An empty value and an absent value can behave differently, so include both in your test cases.
Preserve literal dollar signs intentionally
Shell expressions and passwords can contain dollar signs. Compose interpolation can interpret dollar-prefixed text unless it is handled according to the documented escaping rules. Test a harmless literal value rather than experimenting with a real secret.
Also inspect whether a command is executed through a shell. A value preserved by Compose can be expanded later by a shell inside the container. There can be two distinct expansion stages, each with its own syntax.
Use the .env Formatter for basic dotenv structure checks on a sanitized file. It isn't a Compose evaluator and cannot tell you the final value after every stage of expansion.
Keep deployment sources deliberate
Avoid relying on an operator's incidental shell environment for important release values. Use a documented deployment input and record which environment file is selected. Keep secret values outside version control according to your platform's supported secret-management path.
Compare development and production configurations by field purpose. A difference can be intentional, such as a test service URL, or accidental, such as a missing required setting. Make that distinction explicit in the review.
See YAML implicit types when the rendered value has the right characters but the wrong type. Environment provenance and YAML typing are related boundaries, but they need different checks.
Conclusion
Trace Compose values from interpolation inputs to the resolved service model and finally to the application process. Use synthetic provenance labels, protect rendered secrets and verify that deployment recreated the intended configuration. Clear required values and a documented input path prevent many “it works in my terminal” failures.