# Interpolation and the .env File — Docker Compose

Source: https://www.skillbyai.com/en/docker-compose/c-interp

> ${VAR:-default} at load time.

## Values substituted before anything runs

Compose replaces `${VAR}` in the file with values from your shell environment or from a `.env` file in the project directory, when it loads the file. `${VAR:-default}` uses a default when the variable is unset or empty. **Shell variables win over .env**, which makes one-off overrides easy. Interpolation applies to the Compose file itself; it is different from `environment`, which sets variables inside containers. Commit a `.env.example`, not your real `.env`.

## Configure per machine, validate before running

Variables fill in values at load time; config shows exactly what Compose will do.

![Four ideas: .env interpolation, required variables, precedence, validation.](assets/figures/docker-compose/section-3-map.svg) — Figure 3.1 — Interpolation, required variables, precedence and validation.

## .env values and a shell override, run

I ran this with Docker Compose v2.38.1 and jq in the demo "shop" project. docker compose config parses, interpolates, merges and validates the files without starting containers; the Docker daemon was not running, so nothing was started. With PG_VERSION=16 in .env, the db image resolves to postgres:16; setting PG_VERSION=17 in the shell for one command wins over .env and resolves to postgres:17.

```bash
cat .env
docker compose config --format json | jq -r ".services.db.image"
PG_VERSION=17 docker compose config --format json | jq -r ".services.db.image"
```

Output:

```
DB_PASSWORD=change-me-locally
PG_VERSION=16
postgres:16
postgres:17
```

## Commit .env.example

List every variable with safe example values in .env.example and keep the real .env out of git.

**Quiz:** Which wins when a variable is set both in the shell and in .env?

- [x] The shell environment
- [ ] .env
- [ ] Whichever is longer
- [ ] Neither; Compose errors

*Answer:* The shell environment. Shell values override .env.
