Environment · 02

env_file loads your app settings, but ${…} still uses a fallback

Service environment files and the inputs used to render compose.yaml are different layers. Test both in a single resolved model.

The two values tell the story

Use --env-file for values that must substitute into compose.yaml. Use service-level env_file for variables that belong in the service environment. A service file does not automatically supply every ${…} expression in the Compose model.

A file that is loaded, but not where you expect

# app.env
CC_IMAGE_TAG=9.9
CC_APP_MODE=debug
services:
  probe:
    image: busybox:${CC_IMAGE_TAG:-1.37}
    env_file: app.env
    environment:
      CC_PASSED: ${CC_APP_MODE:-fallback}

Run this in a new directory with no .env and no shell variables named CC_IMAGE_TAG or CC_APP_MODE:

docker compose config --format json

These are the fields our real model check returned:

image: busybox:1.37
environment:
  CC_IMAGE_TAG: "9.9"
  CC_APP_MODE: debug
  CC_PASSED: fallback

The evidence is in the combination: the app variables are present, so app.env was read. Yet the image uses 1.37 and CC_PASSED uses fallback. Neither expression took its input from the service’s environment.

Give interpolation a deliberate source

Put model inputs in a separate file, then select it explicitly. In this example the image tag is an illustration, not a claim that every chosen tag exists.

# model.env
CC_IMAGE_TAG=1.37
CC_APP_MODE=debug
docker compose --env-file ./model.env config

The --env-file flag belongs before config. It changes the interpolation input for this command. Keep env_file: app.env only if those values also belong in the service.

A shell export can still override an interpolated value. If the output differs from your selected file, inspect the specific variable in your current shell or use the isolated precedence experiment linked below. Do not print your whole environment into a public issue.

Do not fix the wrong file

  • Changing app.env can alter a service variable while leaving an image expression unchanged.
  • Changing the interpolation input can change the image while leaving a literal service variable unchanged.
  • A correct resolved model is a prerequisite, not proof that an existing container was recreated with it.

Restore the original files to undo this scratch-directory experiment. No container was started in our test. Our proof covers Compose 5.3.0 and these explicit assignments; it does not test Swarm deployment or an application’s own configuration precedence.

Sources & evidence

Documentation checked 12 Sep 2026. Our fixture evidence:

Continue the investigation