GuidesRunning & maintaining

Docker Compose .env vs env_file: two different things

One fills placeholders in your compose file; the other pours variables into the container. Point env_file at your project .env and host paths ride into an environment where they're wrong.

August 10, 2026Updated September 8, 2026The Everpod team
The short answer

Docker Compose has two different mechanisms that both involve a file full of KEY=value lines, and confusing them causes real bugs. The .env file next to your compose.yaml feeds interpolation: its values fill the $${VARIABLE} placeholders inside the compose file itself. The env_file: attribute on a service does something else entirely: it pours a file’s variables into the container’s environment. Same file format, two destinations: the compose file vs the running process.

Mechanism one: filling in the compose file

Per Docker’s docs, an .env file “is a text file used to define variables that should be made available for interpolation when running docker compose up”, and it’s “the default method for setting variables,” loaded automatically from the project root next to compose.yaml (or from elsewhere via --env-file). Its job is configuring the file: which image tag to run, which host directory to mount, which port to publish. None of it reaches the container unless a placeholder carries it there. One more subtlety worth knowing: this substitution is a Compose CLI feature: plain docker run --env-file does no interpolation at all.

Mechanism two: filling in the container

A service’s environment is set by the environment: attribute (inline key-values) or the env_file: attribute, “one or more files that contain environment variables to be passed to the containers.” When the same key appears in both, the documented precedence is clear: environment: beats env_file:, both beat the image’s own ENV defaults, and a docker compose run -e flag beats everything. So when a container sees a value you didn’t expect, walk that ladder top-down: the answer is on one of its rungs.

The leak: one file doing both jobs

The trap is pointing env_file: at the same .env you use for interpolation (tempting, because it’s sitting right there). Do it and every host-side setting rides into the container’s environment wholesale. The classic shape, with OpenClaw in Docker: the interpolation file holds host paths like the state directory that gets volume-mounted (a directory on the host, mounted at /home/node/.openclaw inside), and host-only paths are simply wrong inside the container, where they can confuse software that reads its location from the environment. The lesson in one line: interpolation values describe the host’s world, a container’s environment describes its world, and a variable that belongs in both is the exception, not the rule.

A layout that stays out of trouble

Keep the roles separate and boring. The project .env holds interpolation values (image pins, host paths for mounts, published ports) and never appears in env_file:. What the container needs arrives explicitly: either named one-by-one under environment: (with $${...} placeholders pulling just the values that genuinely cross the boundary, secrets included), or in a separate, deliberately container-facing file. Explicit is the point: the environment: block becomes a readable manifest of everything the process will see, which is also exactly what you want to check when backing up or rebuilding a service, since both files carry secrets and neither belongs in version control.

Your own cloud agent, set up for you.

Everpod runs OpenClaw on a private, always-on computer of its own: set up, secured and backed up, with model usage included. You name your agent, and say hello about fifteen minutes later.

Create your agent

First month half price, then $29/mo · model usage included · cancel anytime

Wondering what you’d do with one? See what a cloud agent can do