Skip to content

Nekspert/Compose-Env-Playbook

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 

Repository files navigation

.env: env_file и environment в Docker Compose — полный разбор


Введение — две разные задачи

Docker Compose использует переменные в двух принципиально разных местах. Понимание разницы уберёт большую часть путаницы.

  1. Подстановка в docker-compose.yml (интерполяция ${VAR}) — когда Compose читает файл и заменяет ${VAR} в полях, например ports, healthcheck, command, image и т.д.
  2. Передача переменных внутрь контейнеракогда пары KEY=VALUE реально попадают в окружение процесса внутри контейнера (вы видите их в env).

Важное: эти две задачи используют похожие имена переменных, но разные источники и разные правила.


1. Откуда берутся значения для ${VAR} в docker-compose.yml (задача A)

Когда Compose встречает ${VAR} — порядок поиска такой:

  1. Переменные shell, откуда запущен docker compose (например, export VAR=...).
  2. Файл .env, рядом с docker-compose.yml, если в shell значение не найдено.
  3. Если указан дефолт: ${VAR:-default} — используется default.
  4. Если ${VAR:?msg} и переменной нет — Compose выдаст ошибку и остановится (с msg).

Ключевой вывод: env_file: не участвует в поиске значений для ${VAR}.


2. Как переменные попадают внутрь контейнера (задача B)

Источники значений внутри контейнера (в порядке приоритета):

  1. environment: в сервисе (docker-compose.yml) — явное задание;
  2. env_file: в сервисе — загрузка файла(ов) KEY=VALUE;
  3. ENV в Dockerfile образа.

Если одно и то же имя задано в environment: и env_file:, то используется значение из environment: (override).


3. Зачем env_file: path и как он работает

Что делает:

  • env_file: - path/to/file читает файл(ы) формата KEY=VALUE и передаёт их внутрь контейнера для данного сервиса.

Ключевые моменты:

  • env_file действует только внутри того сервиса, где он прописан. Другие сервисы такие пары не увидят, если у них не указан тот же env_file.
  • Путь может быть относительным (от папки с docker-compose.yml) или абсолютным.
  • Содержимое читается буквально: подстановки ${...} не выполняются внутри env_file.

Пример postgres.env:

POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres

Пример docker-compose.yml (фрагмент):

services:
  postgres:
    image: postgres:17-alpine
    env_file:
      - ./postgres.env

4. environment: — зачем и в чём разница

environment: — это явный и контролируемый способ задать переменные, которые попадут в контейнер.

Плюсы environment::

  • Чётко видно, какие переменные важны — удобно как документация;
  • Можно использовать подстановку ${VAR} — тогда Compose возьмёт значение из shell или из .env при парсинге;
  • Значения из environment: перекрывают значения из env_file:.

Можно ли обойтись без environment:?

  • Да: если ты используешь env_file: и он содержит все необходимые пары, либо если переменные выставлены в shell и ты хочешь унаследовать их (через - VAR в environment или иным способом).
  • Но environment: полезен для переопределений и для явной документации.

Пример (обе опции вместе):

services:
  postgres:
    env_file:
      - .env
    environment:
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} # явное дублирование/документация
      TZ: Europe/Moscow

5. Ответы на часто задаваемые вопросы (коротко)

  • Зачем писать env_file: path?

    • Чтобы подключить файл с переменными (не называющийся .env или лежащий в другом месте) и передать пары в контейнер данного сервиса.
  • env_file действует только для текущего сервиса?

    • Да. Он применяется только к тому сервису, где указан.
  • Можно ли без environment:?

    • Можно, если env_file: покрывает всё. Но environment: даёт контроль и возможность переопределять.
  • Можно ли использовать переменные из env_file одного сервиса как ${VAR} в docker-compose.yml?

    • Нет. Подстановка ${VAR} (при чтении docker-compose.yml) смотрит только в shell и в .env рядом с compose.

6. Практические сценарии и примеры

Сценарий A — всё в одном .env (подстановка + попадание в контейнер)

.env:

POSTGRES_DB=postgres
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres

docker-compose.yml (фрагмент):

services:
  postgres:
    image: postgres:17-alpine
    env_file:
      - .env               # передаёт пары в контейнер
    environment:
      POSTGRES_DB: ${POSTGRES_DB}  # документирует/гарантирует
    ports:
      - "${POSTGRES_PORT}:5432"  # подстановка из .env

Здесь .env обеспечивает и подстановку (ports), и передачу в контейнер (через env_file или environment).

Сценарий B — общий файл для многих сервисов

services:
  app:
    env_file:
      - shared.env
  worker:
    env_file:
      - shared.env

Оба сервиса получат одинаковые пары внутри контейнеров.

Сценарий C — переопределение значения для одного сервиса

common.env содержит DEBUG=true, но для web нужно DEBUG=false:

services:
  web:
    env_file:
      - common.env
    environment:
      DEBUG: "false"  # перекроет DEBUG из common.env

7. Краткая шпаргалка (cheatsheet)

  • .env — источник для ${VAR} при парсинге docker-compose.yml (shell > .env > дефолт). Не попадает в контейнер автоматически.
  • env_file: — файл KEY=VALUE, который передаётся в контейнер сервиса (scope: только сервис).
  • environment: — явные переменные в docker-compose.yml, попадают в контейнер и перекрывают env_file:.
  • Приоритет (внутри контейнера): environmentenv_fileDockerfile ENV.

8. Рекомендуемые паттерны

  • Для локальной разработки: храните общие значения в .env и подключайте его в env_file или через environment.
  • Для больших проектов: разделяйте на common.env, postgres.env, app.env и подключайте только нужные файлы к соответствующим сервисам.
  • Никогда не коммить секреты в публичный репозиторий. Для секретов используйте секрет-менеджер, Docker secrets или переменные CI.

Что будет, если env_file имеет имя отличное от .env (например .test.env) и мы пытаемся использовать переменные из него для подстановки в docker-compose.yml?

Короткий ответ: переменные из env_file не используются для подстановки ${VAR} при парсинге docker-compose.yml. Если значение есть только в .test.env, а в shell и в проектном .env его нет — подстановка вернёт пустую строку, и это может привести к ошибкам (например, некорректной спецификации ports).

Детали и примеры поведения:

  • Допустим, у тебя есть файл .test.env:
POSTGRES_PORT=5433

и docker-compose.yml с использованием подстановки:

services:
  postgres:
    image: postgres:17-alpine
    ports:
      - "${POSTGRES_PORT}:5432"
    env_file:
      - .test.env
  • При разборе docker-compose.yml Compose ищет ${POSTGRES_PORT} в shell, затем в файле .env рядом с compose. env_file: .test.env при этом не используется для подстановки. Если в shell и в .env переменной нет, ${POSTGRES_PORT} станет пустой строкой.

  • В результате строка "${POSTGRES_PORT}:5432" превратится в ":5432" — это может привести к ошибке при обработке конфигурации (например, invalid port specification) или к неожиданному поведению.

Как избежать проблемы — варианты решения:

  1. Переименовать .test.env в .env (рядом с docker-compose.yml) — тогда переменные будут видны для подстановки и останутся доступны в контейнере при подключении через env_file или при явном указании в environment:.

  2. Экспортировать переменные в shell перед запуском:

export POSTGRES_PORT=5433
docker compose up -d
  1. Использовать флаг CLI --env-file (Compose V2+) — указать файл, который Compose должен использовать для интерполяции при запуске:
docker compose --env-file .test.env up -d
  1. Не полагаться на подстановку в ports — передавать значение явно через environment: (но учти: если ты пишешь POSTGRES_PORT: ${POSTGRES_PORT} в environment:, то снова подстановка ищет shell/.env, а не env_file). То есть этот вариант сам по себе не решит проблему без того, чтобы переменная была доступна для подстановки.

  2. Сделать проверку "fail fast" в Compose с помощью ${VAR:?message} — если переменная не задана в shell/.env, Compose сразу выдаст понятную ошибку:

ports:
  - "${POSTGRES_PORT:?POSTGRES_PORT is required}:5432"

Это удобнее, чем получать непонятную ошибку invalid port specification.

Резюме:

  • env_file: передаёт пары внутрь контейнера, но не участвует в интерполяции ${VAR} при чтении docker-compose.yml.
  • Если файл имеет имя отличное от .env (например .test.env) и переменные есть только в нём, то для ${VAR} они недоступны, если ты не используешь --env-file или не экспортировал их в shell или не переименовал файл в .env.

About

Environment behavior description in README.md file

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors