Docker Compose Interpolation Tester

See what Compose fills in. Put the .env values above a line of three dashes and the compose text below it.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Test. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

An unset variable

Becomes an empty string, and the error you get is about the image name

---
image: myapp:${TAG}

An empty value with the dash form

Set but empty counts as set, so the default is skipped

TAG=
---
image: myapp:${TAG-latest}

A required variable

Compose stops with your message rather than starting something wrong

---
image: myapp:${TAG:?TAG must be set}

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Relying on a variable being set without a default or a required marker

    Compose warns and substitutes an empty string, so the failure appears somewhere else and the warning is off-screen by then.

    Instead:Use ${VAR:-default} where a default makes sense and ${VAR:?message} where it does not.

  2. Using ${VAR-default} and finding the default ignored

    Without the colon the default applies only when the variable is unset. An .env line reading VAR= sets it to empty, which counts as set.

    Instead:Use ${VAR:-default}, which treats empty and unset the same.

  3. Expecting .env to populate the container's environment

    It fills in ${ } in the compose file. Nothing from it reaches the container on its own.

    Instead:Add the variable under environment: or point env_file: at the file.

Compose warns about an unset variable and carries on

It does not stop. The variable becomes an empty string, so image: app:${TAG} becomes app: and the error you get is about the image name.

What an empty substitution actually breaks

image: app: fails with a confusing reference error. ports: "${PORT}:80" becomes ":80", which binds a random host port rather than failing. An environment value becomes empty and the application starts with a blank credential. The warning about the unset variable scrolled past several lines above whatever went wrong.

The colon changes what counts as missing

${VAR:-default} uses the default when the variable is unset OR empty. ${VAR-default} uses it only when UNSET, so a variable set to nothing, which is what an .env line reading NAME= gives you, keeps its empty value. That pair is a common source of a default that mysteriously does not apply.

Make it required rather than defaulting it

${VAR:?message} stops Compose with your message instead of continuing with an empty value. For anything where there is no sensible default, that is a better failure than a container that starts wrong.

The .env file feeds the compose file, not the container

Compose reads .env from the project directory and uses it to fill in ${ } while parsing the YAML. Nothing in it reaches the container unless the compose file also passes it through environment: or env_file:. Both files look like lists of environment variables, which is exactly why this is confusing.

$$ passes a literal dollar through

Which is how a shell variable, a cron entry or an awk script survives being written in a compose file. A command that works in a terminal and fails under Compose is usually missing these.

The bare $NAME form works too

And is interpolated identically. The braces are only required when the name is followed by characters that could be part of it.