Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Docker Compose Environment Variables: env_file, .env, and Substitution

Sean

Platform Writer

Jul 05, 2026
5 min read

Docker compose environment variables come from three sources per the Docker Compose docs: inline environment: in compose.yaml, env_file: directive (loads .env-style file into container), and .env file in the project directory (for variable substitution in compose.yaml itself). The team that uses env_file for app config and .env for compose-time substitution has the right separation.

Docker Compose Environment Variables: env_file, .env, and Substitution

Table of contents

The environment key

Inline in compose.yaml:

services:
  app:
    environment:
      DATABASE_URL: postgres://db/app
      LOG_LEVEL: info

Or list syntax:

services:
  app:
    environment:
      - DATABASE_URL=postgres://db/app
      - LOG_LEVEL=info

The team that uses inline env vars for dev has quick visibility. The team that uses env_file for prod has separation.

The env_file directive

services:
  app:
    env_file:
      - .env.app
      - .env.secrets

Each file is loaded into the container’s environment. .env.app for config, .env.secrets for sensitive values. The team that uses env_file has secrets separate from compose.yaml (which can be in git).

The .env file for substitution

Compose reads .env from the project directory for variable substitution in compose.yaml:

# .env
POSTGRES_VERSION=16
APP_PORT=8080

# compose.yaml
services:
  db:
    image: postgres:${POSTGRES_VERSION}
  app:
    ports:
      - "${APP_PORT}:8080"

The team that uses .env for substitution has one place to configure compose-time values.

Variable substitution order

Compose substitutes variables from (in order):

  1. OS environment.
  2. .env file in project directory.
  3. .env file passed via --env-file.

OS env wins over .env. The team that uses OS env for overrides has dev/prod differentiation without changing files.

CLI override

docker compose --env-file .env.production up
docker compose run -e DEBUG=true app

The team that uses CLI overrides has runtime config without changing compose files.

Reading compose.yaml env from container

After container starts, env vars are visible via:

docker compose exec app env
docker compose exec app printenv DATABASE_URL

The team that uses this debugs config issues.

Secrets management

For real secrets, don’t use .env (committed by mistake):

  • Docker secrets (Swarm mode).
  • External secret managers (Vault, AWS Secrets Manager).
  • docker compose --env-file from a secrets manager.

The team that uses Vault Agent Injector pulls secrets at runtime, never in compose files.

FAQ

What’s the difference between .env and env_file?

.env is for compose.yaml variable substitution (read by docker compose CLI). env_file is loaded into the container’s runtime environment.

Can I use ${VAR:-default} syntax?

Yes - docker compose supports shell-style default values. The team that uses ${VAR:-latest} has fallback values for missing env.

Do env vars override Dockerfile ENV?

Yes - compose env vars override Dockerfile ENV directives. The team that uses compose overrides has flexible config.

Can I encrypt env_file contents?

Not directly with compose. Use Docker secrets (Swarm) or external secret managers. The team that has sensitive env_file uses proper secret management.

How do I see what env vars are set in a container?

docker compose exec <service> env lists all env vars. The team that uses this for debugging has visibility.

If you are sizing the infrastructure for the kind of project this post covers, the RunxBuild hosting calculator is the right place to model the line items. The compute, the memory, the storage, the bandwidth, the database - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers. The RunxBuild dashboard is where the team sees the actual usage in one place.

Useful related references:

#docker#compose#env#dev-infra