SSD Nodes Learn 8GB RAM — $66/год
Руководства Matt ConnorАвтор: Matt Connor · Обновлено 2026-08-01

Разница между .env, env_file и environment в Docker

Узнайте, чем отличаются .env, env_file и environment в Docker Compose. Разберитесь в приоритетах переменных и поймите, почему секреты нельзя хранить в обычных файлах.

Три сущности, которые называют env-файлом

Docker Compose использует три различных механизма с похожими названиями. Файл .env заполняет плейсхолдеры ${VARIABLE} внутри самого compose.yaml до того, как Compose начнет синтаксический разбор файла. Атрибут env_file: загружает файл с парами ключ-значение в переменные окружения контейнера. Атрибут environment: задает переменные непосредственно для контейнера, прописанные в файле compose. Они не являются взаимозаменяемыми, и если два из них задают один и тот же ключ, приоритет определяется документированным порядком очередности.

В этом руководстве показан принцип работы каждого из них, доказан приоритет с помощью команды, которую вы можете запустить, а затем рассмотрена наиболее важная часть: переменные окружения доступны для чтения любому, кто может запустить docker inspect, поэтому пароли в них хранить нельзя. Если вы новичок в работе с файлами compose, начните с Основы Docker Compose на VPS и вернитесь сюда для настройки конфигурации.

Файл .env предназначен для compose-файла, а не для контейнера

Создайте каталог и поместите в него два файла.

mkdir -p ~/envdemo && cd ~/envdemo
printf 'ALPINE_TAG=3.20\n' > .env
services:
  demo:
    image: alpine:${ALPINE_TAG}
    command: printenv ALPINE_TAG

Теперь запросите у Compose, что именно было проанализировано.

docker compose config

Вывод показывает image: alpine:3.20. Заполнитель исчез, так как интерполяция произошла во время парсинга. Compose ищет .env в каталоге проекта, то есть в каталоге, где находится compose-файл, и подставляет каждое найденное значение ${NAME}.

Затем запустите службу.

docker compose run --rm demo

printenv ALPINE_TAG завершается со статусом 1 и ничего не выводит. Переменная внутри контейнера отсутствует. Это самое распространенное заблуждение: .env настраивает compose-файл, а не процесс. Файл .env, содержащий POSTGRES_PASSWORD=hunter2, никак не влияет на вашу базу данных, если только какая-либо часть compose-файла не ссылается на него.

${NAME:-default} обеспечивает резервное значение, если переменная не задана или пуста. ${NAME:?message} заставляет Compose отказаться от запуска и вывести ваше сообщение, что является правильным выбором для значения, у которого нет безопасного стандартного варианта.

env_file загружает переменные в контейнер

Атрибут env_file: указывает один или несколько файлов, содержимое которых становится переменными окружения контейнера.

printf 'GREETING=from_env_file\nAPP_MODE=production\n' > app.env
services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
docker compose run --rm demo

Эта команда выводит from_env_file. Формат файла представляет собой обычные строки KEY=value, по одной на строку, где # обозначает начало комментария. Это не оболочка (shell). В большинстве случаев кавычки сохраняются как часть значения, а префиксы export не требуются. Не ставьте пробелы вокруг знака =, так как KEY = value создает переменную с буквальным именем KEY , содержащую пробел в начале значения.

Отсутствие пути env_file является ошибкой, и Compose останавливает выполнение. Отметьте файл как необязательный, если его отсутствие является допустимым:

    env_file:
      - path: ./app.env
        required: false

Установка переменных среды в строке

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    environment:
      GREETING: from_environment

Поддерживаются два синтаксиса: форма сопоставления, приведенная выше, и списочная форма с использованием - GREETING=from_environment. Они работают идентично. У списочной формы есть одна дополнительная особенность: ключ без значения передает переменную из оболочки, в которой вы запустили docker compose.

    environment:
      - GREETING
GREETING=from_my_shell docker compose run --rm demo

Это выведет from_my_shell. Если запустить команду, не установив GREETING в оболочке, Compose не установит ничего и не выдаст предупреждение. О подобных случаях «тихого» пропуска переменных стоит знать, так как служба, запускаемая с пустой переменной пароля, часто успешно стартует, оставаясь при этом полностью открытой.

Приоритет конфигурации

Docker определяет порядок приоритета, начиная с самого высокого: docker compose run -e в командной строке, затем environment или env_file, значение которых берется из вашей оболочки или файла переменных окружения, затем обычное environment в файле compose, затем env_file, и в конце директива ENV, встроенная в образ.

Кратко для повседневной работы: environment: имеет приоритет над env_file:, а -e в командной строке перекрывает их оба. Проверим это в одном файле.

services:
  demo:
    image: alpine:3.20
    command: printenv GREETING
    env_file:
      - ./app.env
    environment:
      GREETING: from_environment
docker compose run --rm demo
docker compose run --rm -e GREETING=from_cli demo printenv GREETING

Первая команда выводит from_environment, так как environment: переопределило значение в app.env. Вторая команда выводит from_cli. Никакие параметры в файле compose не переопределяют значения из командной строки.

Если контейнер ведет себя так, будто ваша конфигурация не была применена, не пытайтесь угадать причину. docker compose config выводит полностью разрешенный файл, а docker compose config --environment показывает переменные интерполяции, которые использует Compose. Большинство жалоб на то, что «файл переменных окружения игнорируется», связаны с тем, что значение задано дважды на разных уровнях.

Почему переменные окружения утекают

Установите пароль в environment:, и он будет сохранен в конфигурации контейнера на диске, доступной любому пользователю из группы docker.

docker compose run -d --name leaky -e DB_PASSWORD=hunter2 demo sleep 300
docker inspect leaky --format '{{json .Config.Env}}'

Вывод содержит "DB_PASSWORD=hunter2" в открытом виде. Еще три пути раскрывают это же значение. docker compose config выводит его в терминал, из-за чего оно попадает на форумы технической поддержки. Любой процесс внутри контейнера может прочитать /proc/1/environ, а каждый дочерний процесс наследует эту переменную. Кроме того, обработчики аварийного завершения приложений регулярно записывают всё окружение в журнал или отчет об ошибке.

Членство в группе docker фактически равносильно правам root на хосте, поэтому на эту границу привилегий полагаться нельзя. В руководстве по использованию учетных записей с минимальными привилегиями на VPS объясняется, почему доступ к этой группе следует ограничивать на любом общем сервере.

Хранение секретов Compose в файле

Compose поддерживает секреты на основе файлов. Значение монтируется в контейнер как файл, а не передается через переменные окружения.

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
secrets:
  db_password:
    file: ./db_password.txt

Секрет монтируется по пути /run/secrets/db_password внутри контейнера. Имя после косой черты соответствует имени секрета из блока верхнего уровня secrets:.

Суффикс _FILE — это соглашение, используемое официальными образами Docker, включая postgres, mysql и mariadb. Скрипты точки входа (entrypoint) в этих образах проверяют наличие VARNAME_FILE, считывают файл и используют его содержимое. Это не является функцией Docker, поэтому данный механизм работает только в тех образах, где он реализован. Перед использованием проверьте документацию образа, чтобы убедиться, что SOMETHING_FILE будет обработан. Приложения, которые не поддерживают этот механизм, часто могут самостоятельно прочитать файл при запуске, либо вы можете передать путь к файлу и обработать его в собственном скрипте точки входа.

Проверьте содержимое изнутри запущенного контейнера:

docker compose exec db cat /run/secrets/db_password
docker compose exec db printenv POSTGRES_PASSWORD

Первая команда выводит пароль. Вторая команда ничего не выводит, так как значение не было передано в переменные окружения. В этом и заключается основная цель: docker inspect для этого контейнера отображает только безопасный путь.

Защитите исходный файл на хосте, так как уровень конфиденциальности секрета ограничен уровнем доступа к файлу, в котором он хранится:

chmod 600 db_password.txt

Прагматичный подход к настройке VPS

Многие образы для самостоятельного хостинга не поддерживают переменные _FILE, поэтому единственным способом передачи конфигурации остаются переменные окружения. На VPS с одним администратором реалистичная цель состоит в том, чтобы значения не хранились в файлах с правами на чтение для всех пользователей в каталоге проекта и не попадали в git.

sudo install -o root -g root -m 600 /dev/null /etc/myapp/app.env
sudo nano /etc/myapp/app.env
    env_file:
      - /etc/myapp/app.env

Команда install -m 600 создает файл с уже установленными правами доступа, поэтому не возникает промежутка времени, когда он доступен для чтения всем. Владельцем файла является root, поэтому непривилегированный пользователь на сервере не может его прочитать, хотя любой, кто может выполнить docker, все равно способен извлечь значение из контейнера. Добавьте *.env и .env в .gitignore, а в репозиторий закоммитьте app.env.example, содержащий только имена ключей с пустыми значениями. Закоммиченный пароль считается скомпрометированным и подлежит смене.

Смена значения требует перезапуска службы. Переменные окружения считываются один раз при запуске процесса контейнера, поэтому редактирование файла не дает эффекта до выполнения docker compose up -d --force-recreate db. Этот шаблон используется в руководстве n8n за HTTPS на VPS, где ключ шифрования хранится вне файла compose.

Разделение конфигурации по окружениям

По умолчанию Compose считывает .env из директории проекта. Укажите другой путь с помощью --env-file.

docker compose --env-file .env.staging config

Несколько файлов считываются последовательно, при этом последующие файлы переопределяют предыдущие. Храните настройки по умолчанию, не содержащие секретных данных, в файле, который добавляется в репозиторий, а секреты — в файле, который никогда не покидает сервер. То же самое относится к env_file:, где при наличии дублирующихся ключей приоритет имеет последний указанный файл.

FAQ

Почему мой файл .env игнорируется внутри контейнера?

Он не игнорируется. Файл .env лишь подставляет значения в плейсхолдеры ${NAME} внутри файла compose. Он не задает переменные внутри контейнера. Чтобы передать значение в контейнер, укажите его через environment: { KEY: "${NAME}" } или используйте env_file: ./that-file.env.

Что имеет приоритет: environment или env_file?

environment: имеет приоритет. Согласно документации Docker, атрибут environment стоит выше атрибута env_file, а оба они уступают docker compose run -e при передаче через командную строку. Если ключ задан в обоих местах, значение в env_file игнорируется без вывода предупреждений.

Как увидеть итоговое значение, которое будет использовать Compose?

Выполните docker compose config, чтобы вывести полностью разрешенный файл compose со всеми примененными подстановками. Для уже запущенного контейнера команда docker inspect <container> --format '{{json .Config.Env}}' покажет именно те значения, которые получил его процесс.

Зашифрованы ли секреты в Compose?

Нет. Файловый секрет монтируется в контейнер как обычный файл по пути /run/secrets/<name>, а исходный файл хранится на диске хоста в незашифрованном виде. Преимущество заключается в области видимости, а не в шифровании: значение не попадает в переменные окружения контейнера, в вывод docker inspect и в дампы памяти при сбоях, которые содержат переменные окружения.

Можно ли использовать кавычки и пробелы в файле env?

Используйте KEY=value with spaces и не ставьте кавычки. Compose воспринимает всю оставшуюся часть строки как значение, поэтому кавычки обычно становятся частью самого значения. Никогда не ставьте пробелы вокруг =, так как в этом случае ключ будет содержать завершающий пробел, и совпадений с ним не будет.