Когда мы запускаем контейнер, в большинстве случаев предварительно нужно указать настройки: порт, пароль, режим работы, адрес базы и т. д. Зачастую такие параметры жестко прописывают в самом коде, но это плохой стиль и вообще идея так себе. В будущем вы можете «затроить» и все это слить в git-репозиторий. А как мы знаем, хранить чувствительные данные в там нельзя.
Удобнее и гибче использовать переменные окружения. Те самые, которые environment variables. С помощью переменных можно настраивать поведение контейнера, использовать разные конфигурации (dev/stage/prod), безопасно передавать чувствительные данные. Как видите, одни плюсы.
Работа с переменными в Podman строится практически так же, как в Docker. Есть некоторые нюансы, но о них расскажу чуть позже. Сейчас давайте потыкаем на практике и посмотрим, что же происходит.
Автор статьи — Роман Шубин, CTO и автор Telegram-канала Bash Days.
Вся база
Создаем файл app.py:
import os
name = os.getenv("APP_NAME", "Unknown App")
print(f"Hello from {name}!")
И рядом файл Containerfile:
FROM python:3.11-slim
WORKDIR /app
COPY app.py .
CMD ["python", "app.py"]
Собираем образ:
podman build -t myapp .
Проверяем, все ок:

Теперь самое интересное — давайте передадим переменную APP_NAME в контейнер:
podman run --rm -e APP_NAME="Podman Demo" myapp
В консоли получаем Hello от переменной, как и должно быть:

В большинстве случаев это развязывает руки и делает весь процесс универсальным. Если же переменную не передать, то получим сообщение по умолчанию, уже от Unknown App, как мы указали при создании файла app.py:

Хорошо, первый этап погружения успешно пройден.
Передача переменных через файл .env
Тут все прямо как в Docker!
podman run --rm --env-file .env myapp

Через параметр, --env-file я передал файл .env, в котором у меня содержится набор нужных переменных:
APP_NAME="Podman Demo"
HTTP_PORT=8080
LOGIN=123456789
PASSWORD=pa$$w0rd
С таким подходом нам не нужно раздувать командную строку кишкой из переменных — прописываем все необходимое в этот файл и затем просто подкидываем его в контейнер.
Не забываем в .gitignore добавить исключение для .env файлов. Ну а чтобы прям уж профессионально сделать, создаем рядом env.example и забиваем его теми же переменными, но тестовыми. Такой файл можно запушить в репозиторий. Он будет служить шаблоном для людей, которые захотят развернуть контейнер.
Переменные можно переопределять:
podman run --env-file .env -e APP_NAME=true myapp

Приоритет отдается переменной, которую указали через командную строку. Хотя в файле .env переменная APP_NAME существует и конкретно задана. Прям все, как в Ansible с extra-vars. Кто в курсе, тот в курсе.
Quadlet
Его мы подробно разобрали в статье «Healthcheck инструментами Podman», так что детали и теорию опущу.
В файл с юнитом, например /etc/containers/systemd/demo.container, добавляем:
[Container]
Environment=APP_NAME=QuadletApp
Environment=APP_ENV=production
Environment=DEBUG=false
Проверяем:
podman exec 23828685bd1b env

Все отлично: переменные, указанные в юните, успешно передались. Это полный аналог команды:
podman run -e APP_NAME=QuadletApp -e APP_ENV=production -e DEBUG=false demo
Но опять же логичнее использовать файл, особенно если переменных много. В юните это выглядит так:
EnvironmentFile=%h/dev/app/.env
%h — это домашний каталог пользователя.
Тут главное не ошибиться в путях. Так работать не будет:
EnvironmentFile=.env
Переменные в юните можно переопределить:
[Container]
EnvironmentFile=%h/dev/app/.env
Environment=DEBUG=true
DEBUG=true перезапишет значение из файла.
Нюанс. Если прав на файл с переменными не хватит, то в контейнер ничего не передастся. Тут лучше сразу на берегу этот момент проверять либо смотреть по логам, чтобы варнингов и алертов не было. Особенно это критично с SELinux, он здорово палки в колеса вставляет. Это не страшно, но внимательным стоит быть.
Нормальная работа с секретами в Podman
Когда вы передаете секреты через переменные, их можно увидеть через inspect. Секреты могут попасть в логи, а это уже такое себе.
Podman Secrets — это отдельное хранилище чувствительных данных, которые не передаются как env-переменные, а монтируются в контейнер как файл. Внутри контейнера он обычно лежит здесь:
/run/secrets/<имя>
Создаем секрет:
echo "superpassword" | podman secret create db_password -
Проверяем, что он есть. В данном примере в столбце NAME видим наш db_password:

Запускаем контейнер с секретом:
podman run --rm --secret db_password alpine cat /run/secrets/db_password
В результате видим superpassword:

Если сделать inspect, то мы ничего интересного не увидим — наш суперсекретный пароль не будет раскрыт. По-моему, это отличная фича. Когда я о ней узнал, радовался как ребенок.

Внутри приложения можно дергать эти секреты, например на Python:
DB_PASSWORD = open("/run/secrets/db_password").read().strip()
Это не env-переменная, она доступна только внутри контейнера и только для чтения. Путь хранения файла с секретами можно переопределить:
--secret db_password,target=/tmp/pass
Удаление секрета:
podman secret rm db_password
Внутри Quadlet-юнита это выглядит так:
[Container]
Secret=db_password
А что не нужно помещать в секреты? Логичнее туда не пихать: порты, режимы (debug и т. п.), IP, имена сервисов. В общем, все, что не секретное. Не засоряйте хламом, потом проще будет ориентироваться. Хлам храни в .env-файлах.
- env-переменные → для конфигурации,
- secrets → для чувствительных данных.
В Docker такое тоже было, но если память не изменяет, работало оно только в Swarm. А Swarm, как мы знаем — мертвое легаси, которое лучше не трогать. Так что если у вас есть на борту Podman, у вас развязаны руки. Интересных фич — прям вагон и телега. Главное — о них от кого-то узнать. Например, от меня.
Остатки
Пройдемся по тому, что не уместилось в предыдущие разделы.
Использование переменных в CMD
Можно использовать переменные прямо в CMD или ENTRYPOINT.
CMD ["sh", "-c", "echo Running on port $PORT"]
Но тут есть еще один нюанс: shell-форма будет работать, а exec python — нет. Если знаете, почему, расскажите в комментариях.
Переменные на этапе сборки (ARG vs ENV)
ARG VERSION=2.0
ENV APP_VERSION=$VERSION
ARG будет использоваться только при сборке контейнера. ENV — внутри контейнера.
podman build --build-arg VERSION=2.0 .
Наследование переменных хоста
Можно прокинуть переменную прямо из системы:
export APP_ENV=production
podman run -e APP_ENV myapp
Переменные в Compose сегодня рассматривать не будем, это тема следующей статьи.
Best practices
- не храним секреты в
.env, - используем EnvironmentFile для systemd,
- разделяем env по окружениям (.env.dev, .env.prod),
- не хардкодим значения в Containerfile,
- проверяем через Podman inspect,
- пользуемся Podman secrets.
Подведем итоги
На первый взгляд, переменные в Podman — это просто -e и .env, но на практике вокруг них строится вся конфигурация контейнера. Чем раньше вы начнете правильно работать с ними, тем проще вам будет масштабировать и поддерживать свои сервисы.
The End!