Quadlet — это генератор systemd, который входит в состав Podman и превращает декларативные файлы в обычные службы. В 2026 году это основной способ автозапуска контейнеров: старый podman generate systemd объявлен устаревшим, а Quadlet не требует отдельных пакетов и работает как для root, так и для обычного пользователя. Ниже — практическая схема от первого файла до диагностики, обновления образов и ограничения прав.

Как Quadlet превращает файл в сервис

Генератор читает файлы с расширениями .container, .volume, .network, .pod, .build, .image и .kube при загрузке системы и при каждом systemctl daemon-reload. Из файла web.container получается служба web.service. Для rootful-режима файлы лежат в /etc/containers/systemd/, для rootless — в ~/.config/containers/systemd/. Временные файлы можно класть в /run/containers/systemd/, но после перезагрузки они исчезнут.

Quadlet требует cgroup v2. Проверьте это до начала работы, иначе контейнер может запуститься, но управление ресурсами и часть настроек systemd будут вести себя непредсказуемо.

podman info --format '{{.Host.CgroupsVersion}}'
mkdir -p ~/.config/containers/systemd
systemctl --user daemon-reload
systemctl --user is-system-running

Минимальный файл .container

Обязателен только Image, но для рабочего сервиса сразу задайте имя, порт и том. Директивы [Container] почти один в один соответствуют ключам podman run, а секции [Unit], [Service] и [Install] достаются systemd без изменений.

[Unit]
Description=Web frontend

[Container]
Image=docker.io/library/nginx:1.27-alpine
ContainerName=web
PublishPort=127.0.0.1:8080:80
Volume=web-data.volume:/usr/share/nginx/html:Z

[Service]
Restart=always

[Install]
WantedBy=default.target

После сохранения выполните daemon-reload и запустите службу. Важная особенность: включить сгенерированный юнит через systemctl enable нельзя — он создаётся генератором на лету и не лежит постоянным файлом в /etc/systemd/system. Вместо этого генератор сам применяет секцию [Install] и создаёт зависимости при загрузке, поэтому строки WantedBy=default.target достаточно.

systemctl --user daemon-reload
systemctl --user start web.service
systemctl --user status web.service --no-pager
journalctl --user -u web.service -n 50 --no-pager

Если система пишет Unit web.service not found, значит генератор не смог разобрать файл. Типичные причины — опечатка в имени директивы, ключ из более новой версии Podman или отсутствующий образ.

Тома, сети и pod

Именованный том удобнее описывать отдельным файлом .volume: тогда Podman создаст его до запуска контейнера и свяжет службы зависимостью. В ссылке указывается имя файла с расширением, а не имя тома. Сеть описывается так же — отдельным .network.

[Volume]
VolumeName=web-data

[Network]
NetworkName=appnet
Subnet=10.89.10.0/24

В контейнере используйте Volume=web-data.volume:/data:Z и Network=appnet.network. Суффикс :Z включает приватную SELinux-метку; на системах без SELinux он игнорируется. Для пода создайте app.pod и подключите контейнеры через Pod=app.pod — так они разделят сетевой namespace и порты.

[Pod]
PodName=app
PublishPort=127.0.0.1:8080:80

[Container]
Image=docker.io/library/nginx:1.27-alpine
Pod=app.pod

Не публикуйте порты на 0.0.0.0 без необходимости. Привязка 127.0.0.1:8080:80 оставляет сервис доступным только локально, а наружу его лучше отдавать через reverse proxy.

Автозапуск без входа в систему

Rootless-службы живут в пользовательском менеджере systemd и останавливаются, когда пользователь выходит из системы. Чтобы контейнер пережил logout и запускался после перезагрузки, включите linger.

loginctl enable-linger "$USER"
loginctl show-user "$USER" -p Linger
systemctl --user daemon-reload
systemctl --user is-system-running

Для rootful-режима файлы кладут в /etc/containers/systemd/, а в [Install] указывают WantedBy=multi-user.target. Права на файлы должны быть 0644, владелец root:root. Не держите в юните пароли и токены: используйте EnvironmentFile с правами 0600 или Podman secrets.

Healthcheck и обновление образов

AutoUpdate=registry заставляет podman auto-update проверять реестр. Это удобно для домашних сервисов, но для критичных систем лучше фиксировать тег или digest и обновляться вручную после чтения changelog. Healthcheck позволяет systemd перезапускать контейнер, который жив, но не отвечает.

[Container]
Image=docker.io/library/nginx:1.27-alpine
AutoUpdate=registry
HealthCmd=wget -q -O /dev/null http://localhost/ || exit 1
HealthInterval=30s
HealthRetries=3
HealthOnFailure=kill

Проверьте таймер автообновления и заранее посмотрите, что будет обновлено. Команда podman auto-update --dry-run ничего не меняет и показывает план.

podman auto-update --dry-run
systemctl --user list-timers podman-auto-update.timer
podman healthcheck run web
podman inspect web --format '{{.State.Health.Status}}'

Диагностика

Самый полезный инструмент — dryrun генератора: он печатает, какие юниты будут созданы, и показывает ошибки разбора. Путь к генератору зависит от дистрибутива. systemd-analyze с флагом --generators=true дополнительно проверяет сгенерированный юнит целиком. Дальше смотрите статус службы и логи Podman.

/usr/lib/systemd/system-generators/podman-system-generator --user --dryrun
systemd-analyze --user --generators=true verify web.service
podman ps --format '{{.Names}} {{.Status}}'
podman logs --tail 50 web

Если контейнер стартует, но приложение недоступно, проверьте, слушает ли оно нужный адрес внутри образа, и совпадает ли опубликованный порт. podman inspect показывает фактические переменные окружения и монтирования, а не только то, что написано в файле.

Безопасность и типичные ошибки

Главное преимущество Quadlet — возможность запускать контейнеры без root. Дополнительно закройте лишние возможности: ReadOnly=true, DropCapability=ALL, NoNewPrivileges=true, Tmpfs=/tmp. Не используйте --privileged и монтирование docker.sock: это фактически отдаёт контейнеру управление хостом.

Отдельно задайте лимиты: Memory=, PidsLimit= и TimeoutStartSec. Без них контейнер с утечкой памяти выест хост, а первый запуск с загрузкой большого образа упрётся в стандартные 90 секунд и завершится ошибкой. TimeoutStartSec=900 в секции [Service] даёт запас на pull. Для наблюдения используйте systemd-cgtop и podman stats: они показывают, какой юнит действительно расходует ресурсы, и помогают отличить проблему приложения от нехватки прав.

  • Ключ User= в секции [Service] не понижает права контейнера: rootless-режим определяется тем, от какого пользователя запущен генератор. Внутри образа используйте User= и Group= из [Container].
  • Для томов на SELinux ставьте :Z, иначе приложение получит permission denied.
  • Порты ниже 1024 в rootless-режиме требуют настройки net.ipv4.ip_unprivileged_port_start; проще слушать 8080 и проксировать.
  • После правки файла всегда выполняйте daemon-reload, иначе изменится только исходник, а не служба.

Итог

Quadlet даёт предсказуемый автозапуск без shell-обёрток и cron. Начните с rootless-файла, проверьте dryrun, включите linger и только потом добавляйте healthcheck и автообновление. Такой порядок экономит время при отладке и не оставляет контейнеры, которые «вроде запущены», но не переживают перезагрузку.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Поделиться: Telegram ВКонтакте