Docker Compose — Variable Substitution

1 minute read

Docker Compose supports shell-style variable substitution in compose.yml, powered by .env files or host environment variables.

Basic Substitution

services:
  db:
    image: postgres:${POSTGRES_TAG}

If POSTGRES_TAG=16 is set (in .env or shell), this resolves to postgres:16.

Default Values

services:
  db:
    image: postgres:${POSTGRES_TAG:-16}     # Default: 16 if unset or empty
  api:
    image: myapi:${API_TAG-latest}           # Default: latest if unset only
Syntax Meaning
${VAR} Substitute value
${VAR:-default} Default if unset or empty
${VAR-default} Default if unset only

Error on Missing

services:
  db:
    image: postgres:${POSTGRES_TAG:?Error: POSTGRES_TAG must be set}

If POSTGRES_TAG is unset or empty, Compose shows:

Error: POSTGRES_TAG must be set
Syntax Meaning
${VAR:?error} Fail if unset or empty
${VAR?error} Fail if unset only

Replacement When Set

services:
  api:
    environment:
      DEBUG: ${DEBUG:+enabled}    # "enabled" if DEBUG is set and non-empty
Syntax Meaning
${VAR:+replacement} Replacement if set and non-empty
${VAR+replacement} Replacement if set (even if empty)

Escaping Dollar Signs

Use $$ to output a literal $:

services:
  api:
    environment:
      GREETING: "Price is $$5.00"   # Resolves to: Price is $5.00

Priority of Variable Sources

Priority Source
1 (highest) Shell environment variable
2 .env file
3 Default in compose.yml (${VAR:-default})

Verify with docker compose config

Always verify substitution:

docker compose config

This outputs the fully resolved compose file.

Complete Example

.env:

PROJECT=myapp
DB_TAG=16
REDIS_TAG=7
API_PORT=3000

compose.yml:

services:
  db:
    image: postgres:${DB_TAG:-15}
    container_name: ${PROJECT}-db
  cache:
    image: redis:${REDIS_TAG:?Redis tag required}
  api:
    image: ${PROJECT}-api:latest
    ports:
      - "${API_PORT:-8080}:3000"

Summary

Pattern Meaning Example
${VAR} Substitute postgres:${TAG}
${VAR:-default} Default if unset/empty ${TAG:-16}
${VAR:?error} Error if unset/empty ${TAG:?required}
${VAR:+alt} Alt if set ${DEBUG:+enabled}
$$ Literal $ $$PATH

Quick Reference

Item Detail
${VAR} Substitute variable value
${VAR:-default} Default if unset or empty
${VAR:?error} Fail with error if unset or empty
${VAR:+replacement} Replacement if set and non-empty
$$ Literal dollar sign
docker compose config Verify all substitutions