Environment · 03

Changed the value, got the old one? Trace Compose’s two precedence steps

First resolve the expression. Then inspect the environment assigned to the service. A literal value and ${CC_LEVEL} behave differently.

Start with the exact assignment

If the Compose file says CC_LEVEL: literal, changing a shell variable will not replace that literal. If it says CC_LEVEL: ${CC_LEVEL:-fallback}, the shell can provide the interpolation input. Those are different configurations.

Case 1: a literal beats the service file

# app.env
CC_LEVEL=file
services:
  probe:
    image: busybox:1.37
    env_file: app.env
    environment:
      CC_LEVEL: literal
CC_LEVEL=shell docker compose config

Our resolved environment is CC_LEVEL: literal. The shell did not override this literal, and the explicit service mapping replaced the value from app.env. This is why “the shell always wins” is not a useful diagnosis on its own.

Case 2: the mapping uses interpolation

Now replace only the assignment with:

environment:
  CC_LEVEL: ${CC_LEVEL:-fallback}

For this second fixture, we used .env with CC_LEVEL=dot-env, cli.env with CC_LEVEL=cli-file and app.env with CC_LEVEL=service-file.

Input to the commandResolved CC_LEVEL
Shell CC_LEVEL=shell, --env-file cli.envshell
No shell value, --env-file cli.envcli-file
No shell value, no --env-filedot-env

All three outcomes were measured with the same fixture content. The explicit environment mapping then supplies the resolved value to the service model.

An empty value is still a value

# .env
CC_EMPTY=

# environment fragment
CC_A: ${CC_EMPTY:-fallback}
CC_B: ${CC_EMPTY-fallback}

In our model check, CC_A became fallback; CC_B became an empty string. The colon changes how an empty input is handled. Check for an empty export when a default behaves unexpectedly.

A short debugging sequence

  1. Locate the exact service and key in your resolved model.
  2. Check whether the assignment is literal or interpolated.
  3. For interpolation, compare the specific shell input with the explicitly selected file.
  4. Check whether an environment mapping replaces a service-file value.
  5. If the model is correct, investigate the running container separately.

Our tests stop at model resolution. They do not execute docker compose run -e or test image ENV; those additional runtime precedence rules are covered in Docker’s full reference. Keep production configuration output private.

Sources & evidence

Documentation checked 12 Sep 2026. Our fixture evidence:

Continue the investigation