OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

Podman Quadlet: rootless systemd на VDS — практическое руководство

Quadlet превращает .container/.pod в systemd‑юниты. В связке с rootless Podman и systemd --user это чистый и безопасный способ держать сервисы на VDS без root. Разберём установку, loginctl enable‑linger, сети, тома, автообновления и лимиты — с примерами и подводными камнями.
Podman Quadlet: rootless systemd на VDS — практическое руководство

Если вы привыкли разворачивать контейнеры через Docker Compose, но хотите более нативной интеграции с systemd и безопасного rootless‑режима, обратите внимание на связку Podman + Quadlet. Quadlet — это генератор unit‑ов: вы описываете контейнер в .container/.pod файле, а он создаёт полноценную systemd‑службу, которую удобно запускать как user‑юнит (systemd --user) на вашем VDS. Получается чисто, предсказуемо, без лишних демонов и с удобным журналированием через journald.

Что такое Quadlet и зачем он на VDS

Quadlet входит в Podman и позволяет хранить декларативные описания контейнеров в каталоге пользователя, автоматически генерируя systemd‑юниты. Это избавляет от ручного написания сложных ExecStart, ускоряет онбординг и делает инфраструктуру прозрачнее. На VDS это особенно удобно: вы получаете автозапуск при перезагрузке, единый формат логов, гибкие рестарты, resource‑лимиты через systemd и минимум привилегий благодаря rootless.

Идея проста: вместо «супервизора поверх контейнеров» — сам systemd становится супервизором. Quadlet аккуратно подготавливает unit‑файл, а вы управляете сервисом привычными командами systemctl — только в user‑сессии.

Предварительные требования и версии

Рекомендуемые версии для комфортной работы:

  • Podman 4.4+ (на свежих Linux‑дистрибутивах обычно уже так).
  • systemd 252+ (user‑юниты стабильны; включаем «linger» для автозапуска после ребута).
  • cgroups v2 (на современных дистрибутивах включён по умолчанию).
  • Пакеты: podman, uidmap, slirp4netns, fuse-overlayfs, а также плагины Podman (часто в составе базового пакета) для проброса портов в rootless.

Пути хранения для rootless Podman:

  • Образы и контейнеры: ~/.local/share/containers.
  • Конфиги Quadlet: ~/.config/containers/systemd.

Установка на популярных дистрибутивах

Debian 12 / Ubuntu 22.04+:

sudo apt update
sudo apt install -y podman uidmap slirp4netns fuse-overlayfs

Rocky Linux 9 / AlmaLinux 9 / RHEL 9+:

sudo dnf install -y podman uidmap slirp4netns fuse-overlayfs

Проверьте версии:

podman --version
systemctl --version

Пример .container файла и статус user‑юнита systemd

Выделяем пользователя для контейнеров

Создадим отдельный аккаунт, под которым будут крутиться rootless‑контейнеры (или используйте свой обычный, если так удобнее):

sudo useradd -m -s /bin/bash app
sudo passwd app

Проверьте, что у пользователя есть записи в /etc/subuid и /etc/subgid (обычно добавляются автоматически):

grep ^app: /etc/subuid /etc/subgid || echo "subuid/subgid для app не найдены"

Если диапазонов нет, добавьте вручную безопасный диапазон (пример):

echo "app:100000:65536" | sudo tee -a /etc/subuid
echo "app:100000:65536" | sudo tee -a /etc/subgid

Включаем автозапуск user‑юнитов после ребута через «linger»:

sudo loginctl enable-linger app

Проверьте, что user‑сессия поднимется и без интерактивного логина. Это ключевой момент для systemd --user на VDS.

Мини‑проверка rootless Podman

Заходим под пользователем и запускаем тест:

sudo -iu app
podman run --rm quay.io/podman/hello

Если что‑то не стартует, читайте сообщения: часто не хватает uidmap или возникают проблемы с slirp4netns.

Структура каталогов Quadlet

Подготовим каталог под unit‑описания:

mkdir -p ~/.config/containers/systemd

Любой *.container, *.pod или *.volume файл внутри этого каталога будет обработан Quadlet и превратится в systemd‑юнит после systemctl --user daemon-reload.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Простой контейнер через Quadlet

Создадим демонстрационный сервис на базе образа traefik/whoami. Этот контейнер отвечает на HTTP и показывает сведения о запросе — удобно для проверки сети и портов.

Подготовим директории и переменные окружения:

mkdir -p ~/apps/whoami
cat > ~/apps/whoami/whoami.env << 'EOF'
TZ=UTC
WHOAMI_GREETING=Hello from Quadlet
EOF

Создадим Quadlet‑файл ~/.config/containers/systemd/whoami.container:

[Unit]
Description=Whoami demo (Podman Quadlet rootless)
Wants=network-online.target
After=network-online.target

[Container]
# Образ
Image=traefik/whoami:v1.10
ContainerName=whoami

# Сетевой стек rootless (по умолчанию slirp4netns)
Network=slirp4netns

# Проброс портов: внешний 8080 -> контейнерный 80
# Можно использовать 80:80, если поддерживается rootlessport для низких портов
PublishPort=0.0.0.0:8080:80

# Логи в journald для удобства systemd --user
LogDriver=journald

# Healthcheck через curl внутри контейнера
HealthCmd=curl -fsS http://127.0.0.1:80/ || exit 1
HealthInterval=30s
HealthTimeout=3s
HealthRetries=3

# Переменные окружения
EnvironmentFile=%h/apps/whoami/whoami.env
Environment=WHOAMI_COLOR=green

# Том с данными (пример бинд‑монта)
# Обратите внимание на суффикс :U для корректного маппинга uid/gid при rootless
Volume=/home/app/apps/whoami:/data:U,Z

# Автообновление образа (см. раздел ниже про таймер)
AutoUpdate=registry

[Service]
# Рестарт‑политика на уровне systemd
Restart=always
RestartSec=2s

# Лимиты ресурсов через cgroups v2
MemoryMax=256M
CPUQuota=80%

# Более аккуратное завершение
TimeoutStopSec=30s

[Install]
WantedBy=default.target

Активируем и запускаем:

systemctl --user daemon-reload
systemctl --user enable --now whoami.service
systemctl --user status whoami.service

Проверяем, что сервис слушает порт:

ss -ltnp | grep 8080
curl -fsS http://127.0.0.1:8080/ | head -n 3

Просмотр логов:

journalctl --user -u whoami -f

О портах ниже 1024 в rootless

В rootless‑режиме обычный пользователь не может слушать привилегированные порты (<1024) без специальных возможностей. Podman решает это через вспомогательный бинарник rootlessport с capability cap_net_bind_service. Проверьте:

command -v rootlessport
getcap $(command -v rootlessport)

Если capability присутствует, можете указать PublishPort=80:80 в Quadlet. В противном случае используйте высокие порты (например, 8080) и проксируйте через веб‑сервер на хосте.

Секреты и окружение

Никогда не храните секреты в явном виде внутри .container. Подключайте их через EnvironmentFile=, выставляйте права 0600 и владельца нужного пользователя:

chmod 600 ~/apps/whoami/whoami.env

При обновлении файла окружения просто перезапустите сервис:

systemctl --user restart whoami

Тома и права в rootless

На bind‑монтах в rootless часто «ломаются» владельцы и права внутри контейнера. Для простых кейсов помогает опция :U в Volume=, которая временно ремапит владельцев для контейнера. Если нужна точная контроль над владельцами, используйте опции idmap или заранее подготавливайте права под subuid/subgid.

Если SELinux включён (RHEL‑семейство), добавляйте суффикс :Z или :z к монтам, как в примере выше.

Автообновление образов

Мы указали AutoUpdate=registry в .container. Теперь включим user‑таймер Podman, чтобы периодически подтягивать новые теги и перезапускать сервис:

systemctl --user enable --now podman-auto-update.timer
systemctl --user list-timers | grep podman-auto-update

Можно проверить в «сухом режиме»:

podman auto-update --dry-run

Если вы переходите с cron на таймеры, посмотрите наше руководство по системным таймерам: миграция с cron на systemd timers.

Продакшен‑совет: закрепляйте минорные версии и тщательно тестируйте, прежде чем включать автообновление на критичных сервисах.

Мультиконтейнер через Pod (опционально)

Quadlet поддерживает .pod, что удобно для группировки нескольких контейнеров с общим сетевым пространством. Допустим, нам нужна связка «приложение + БД». Создадим pod и два контейнера, присоединённых к нему.

Файл pod: ~/.config/containers/systemd/app.pod

[Unit]
Description=Application pod (rootless)
Wants=network-online.target
After=network-online.target

[Pod]
PodName=app
# Пробросим порт на хост, остальное оставим внутрь pod
PublishPort=127.0.0.1:5432:5432

[Service]
Restart=always
RestartSec=2s

[Install]
WantedBy=default.target

База данных: ~/.config/containers/systemd/app-db.container

[Unit]
Description=PostgreSQL in pod

[Container]
Image=postgres:16
ContainerName=app-db
Pod=app
Environment=POSTGRES_PASSWORD=secret
Environment=POSTGRES_USER=app
Environment=POSTGRES_DB=app
Volume=/home/app/apps/pgdata:/var/lib/postgresql/data:U,Z

[Service]
Restart=always

[Install]
WantedBy=default.target

Приложение: ~/.config/containers/systemd/app-web.container

[Unit]
Description=Web app in pod

[Container]
Image=ghcr.io/example/web:1.2.3
ContainerName=app-web
Pod=app
Environment=DATABASE_URL=postgres://app:secret@127.0.0.1:5432/app

[Service]
Restart=always

[Install]
WantedBy=default.target

Активируем всё:

systemctl --user daemon-reload
systemctl --user enable --now app.pod app-db.service app-web.service
systemctl --user status app-web

Схема pod с двумя контейнерами и публикацией порта

Подход с pod упрощает межконтейнерные связи и локальную сеть, но помните: rootless‑сеть — пользовательская, и некоторые возможности (например, мультикаст/низкоуровневые сетевые твики) могут быть ограничены.

Лимиты и приоритизация через systemd

Quadlet даёт право указывать системные опции в секции [Service] прямо в .container или .pod. Это открывает аккуратную приоритизацию на VDS: ограничивайте «шумные» сервисы и защищайте критичные.

  • MemoryMax= — жёсткий лимит памяти.
  • CPUQuota= — доля CPU (например, 50% = пол‑ядра на однопоточном CPU).
  • IOWeight= — относительный вес I/O при конкуренции.
  • TasksMax= — ограничение количества потоков/процессов.

Все эти опции работают и в user‑юнитах, если включены cgroups v2 и поддержка в ядре.

Логи и мониторинг

С LogDriver=journald журналы контейнера попадают в journalctl. Это удобно для оперативного анализа и алертов. Типовой набор команд:

journalctl --user -u whoami -e
journalctl --user -u whoami -f
systemctl --user show whoami | sed -n '1,100p'

Если вы предпочитаете файловые драйверы логирования, переключайтесь на k8s-file и читайте логи из каталога контейнера. Но на практике journald плюс фильтрация по unit — очень удобная пара. Для централизованного сбора можно настроить journal-remote: включаем удалённый журнал systemd.

Типичные проблемы и отладка

  • Сервис не стартует после ребута: проверьте loginctl enable-linger app. Без linger user‑юниты не поднимаются автоматически.
  • «cannot find newuidmap/newgidmap»: установите пакет uidmap.
  • Сеть не работает / порты не слушаются: проверьте slirp4netns, наличие rootlessport и его capability. Убедитесь, что вы используете высокие порты, либо включите возможность слушать <1024.
  • Права на томах: добавьте :U к монтам в Volume= или скорректируйте владельцев под subuid/subgid.
  • «XDG_RUNTIME_DIR is not set»: используйте вход через sudo -iu app или полноценный логин, а также убедитесь, что включён linger. Не запускайте systemctl --user из «обезличенных» контекстов без окружения.
  • Healthcheck падает: пересмотрите HealthCmd, интервалы и таймауты. Учтите, что команда выполняется внутри контейнера.

Стратегии обновления

Есть два основных подхода:

  • Ручное: подтянуть новый образ и перезапустить службу.
podman pull traefik/whoami:v1.10
systemctl --user restart whoami
  • Автоматическое: AutoUpdate=registry в .container и активный podman-auto-update.timer. Плюс стратегия тэгов (например, 1.10 вместо latest).

Для нулевого простоя используйте два контейнера и внешний балансировщик или прокси с плавным переключением трафика. В рамках одной user‑службы systemd обеспечит быстрый рестарт, но не «бесшовный».

Безопасность rootless

  • Избегайте --privileged и избыточных capability.
  • Открывайте наружу только необходимые порты. Для общих портов используйте обратный прокси на хосте.
  • Храните секреты в EnvironmentFile с правами 0600, не коммитьте их в репозитории.
  • Ограничивайте ресурсы через systemd, чтобы «шумный» контейнер не мешал соседям на VDS.

Сравнение с Docker Compose на одиночном сервере

  • Плюсы Quadlet: плотная интеграция с systemd, ровные логи, нативные рестарты/лимиты, отсутствие внешнего демона, rootless «из коробки».
  • Минусы: нет привычного YAML‑сценария с зависимостями и переменными (хотя .pod частично это закрывает). Для сложной оркестрации лучше Kubernetes/nomad.

Для одиночного VDS или набора простых сервисов Quadlet часто оказывается проще и надёжнее.

Чек‑лист для продакшена

  • Подтверждена поддержка cgroups v2 и актуальных версий Podman/systemd.
  • Создан отдельный пользователь, включён loginctl enable-linger.
  • Образ закреплён по версии, проверены AutoUpdate и таймеры.
  • Логи сходятся в journald, при необходимости — отправляются в централизованный сборщик (journal-remote).
  • Ресурсы ограничены (MemoryMax, CPUQuota), порты открыты минимально.
  • Бэкапы конфигов: ~/.config/containers/systemd и каталогов с данными приложений.

Удаление и очистка

Корректно останавливаем и вычищаем юнит и данные:

systemctl --user disable --now whoami
rm ~/.config/containers/systemd/whoami.container
systemctl --user daemon-reload
podman ps --all
podman images
# При необходимости удалить образ/контейнер/том
# podman rm -f whoami
# podman rmi traefik/whoami:v1.10

Итоги

Связка Podman + Quadlet даёт удобный путь к управляемым, воспроизводимым и безопасным развертываниям на VDS. Rootless‑режим уменьшает поверхность атаки, а systemd --user с loginctl enable-linger обеспечивает надёжный автозапуск без root. Добавьте к этому healthcheck, journald, автообновления и лимиты ресурсов — и вы получаете аккуратный «мини‑оркестратор» из стандартных компонентов Linux, без лишней магии и постоянных демонов.

Поделиться статьей

Вам будет интересно

rclone serve: S3/WebDAV/HTTP как универсальный шлюз к Object Storage OpenAI Статья написана AI (GPT 5)

rclone serve: S3/WebDAV/HTTP как универсальный шлюз к Object Storage

Покажем, как превратить Object Storage в универсальный сервис с rclone serve: отдача по HTTP, WebDAV и S3, настройка VFS‑кэша и TT ...
fscrypt на ext4: практическое шифрование каталогов на VDS и сравнение с LUKS OpenAI Статья написана AI (GPT 5)

fscrypt на ext4: практическое шифрование каталогов на VDS и сравнение с LUKS

Разбираем нативное шифрование ext4 с fscrypt: чем оно отличается от LUKS на уровне диска, когда какой подход использовать на VDS, ...
HashiCorp Nomad на VDS: альтернатива Kubernetes, jobs/allocations и сервис‑дискавери с Consul OpenAI Статья написана AI (GPT 5)

HashiCorp Nomad на VDS: альтернатива Kubernetes, jobs/allocations и сервис‑дискавери с Consul

Хотите оркестрацию контейнеров и сервисов без сложности Kubernetes? Покажу, как развернуть Nomad на облачных VDS, подключить Consu ...