Rootless Podman запускает контейнеры от обычного пользователя: без демона, без членства в группе docker и без прав root на хосте. Для домашней лаборатории это снимает главный риск: ошибка или уязвимость в контейнере уже не означает компромисс всей машины. Вместе с безопасностью приходят ограничения — нельзя занять порт 80, сложнее с правами на тома, а половина ошибок выглядит как «operation not permitted» без объяснений. Разберём, как rootless-режим устроен, как подготовить систему и что делать, когда Podman отказывается запускать контейнер.
Как работает rootless-режим
В обычном режиме Podman общается с ядром от root, и процессы внутри контейнера с UID 0 соответствуют UID 0 хоста. В rootless всё иначе: Podman стартует от вашего пользователя, создаёт новое user namespace и внутри него действительно становится root. Ядро подменяет идентификаторы — ваш UID 1000 снаружи внутри выглядит как 0, а остальные номера берутся из диапазона, выделенного в /etc/subuid.
Диапазон нужен не только для красоты. Именно он позволяет запускать процессы с разными UID внутри одного контейнера: например, master и worker PostgreSQL или nginx с отдельным пользователем. Если дополнительных диапазонов нет, доступен единственный UID, и образы, которые меняют пользователя через USER в Dockerfile, падают с permission denied.
Запись в uid_map выполняет не сам Podman, а небольшие setuid-хелперы newuidmap и newgidmap из пакета uidmap. Они проверяют, что запрошенные диапазоны выделены именно этому пользователю, и только потом пишут в /proc/PID/uid_map. Поэтому бит setuid на этих файлах — обязательное условие, а не случайность.
Проверка и подготовка системы
Сначала убедитесь, что диапазоны выделены. Обычно это делает useradd, но в минимальных образах и после ручного создания пользователя записи может не быть.
grep "$USER" /etc/subuid /etc/subgid
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 "$USER"
sudo apt install uidmap passt
podman info --format '{{.Host.Security.Rootless}}'
Команда должна вернуть true. Если диапазоны пришлось добавлять, выполните podman system migrate: он пересоздаст отображение для уже существующего хранилища. Следите, чтобы диапазоны разных пользователей не пересекались — это частая причина ошибки write to uid_map failed. Проверить занятые диапазоны можно через sort -t: -k2 -n /etc/subuid.
Хранилище и права на тома
Rootless-контейнеры живут не в /var/lib/containers, а в ~/.local/share/containers/storage. Драйвер хранения по умолчанию — overlay поверх fuse-overlayfs, если ядро и дистрибутив не разрешают непривилегированный overlayfs. Проверить, что реально используется, можно так:
podman info --format '{{.Store.GraphDriverName}}'
podman system df
podman unshare chown -R 1000:1000 ~/data
podman unshare ls -ln ~/data
Если в выводе vfs, каждый слой копируется целиком: место расходуется заметно быстрее, а сборка образов становится медленной. fuse-overlayfs медленнее нативной overlay, но предсказуем и не требует прав root.
С томами есть тонкость. Флаг :U при монтировании рекурсивно меняет владельца каталога на пользователя контейнера, а :Z перемаркирует его для SELinux. В rootless-режиме chown внутри контейнера попадает в subuid-диапазон, поэтому на хосте владельцем каталога окажется, например, UID 100999. Это нормально. Чтобы посмотреть на файлы «глазами контейнера», используйте podman unshare — он входит в то же пространство идентификаторов.
Сеть без привилегий
Контейнер получает собственный network namespace, а трафик наружу проходит через pasta (в Podman 5) или slirp4netns. Внутри контейнера 127.0.0.1 — это сам контейнер, а не хост; для обращения к сервисам хоста есть имя host.containers.internal. Проброс портов выполняет rootlessport: Podman слушает порт от вашего имени и пересылает соединения внутрь.
podman run -d --name web -p 8080:80 docker.io/library/nginx:alpine
curl -I http://127.0.0.1:8080
podman port web
ss -lntp | grep 8080
Порты ниже 1024 в rootless-режиме недоступны: -p 80:80 вернёт bind: permission denied. Вариантов три: поставить обратный прокси на высоком порту, разрешить непривилегированные порты через sysctl net.ipv4.ip_unprivileged_port_start=80 (это делает администратор хоста) или использовать systemd-сокет с передачей дескриптора. Для домашней лаборатории почти всегда достаточно прокси.
Несколько контейнеров удобнее объединить в пользовательскую сеть: podman network create app. В ней работает разрешение имён через aardvark-dns, и контейнеры находят друг друга по имени, а не по IP.
Автозапуск и лимиты
Главная ловушка rootless-режима — сессия пользователя. Без включённого linger systemd завершает пользовательские службы при выходе из системы и останавливает контейнеры. Включить постоянную сессию нужно один раз:
sudo loginctl enable-linger "$USER"
loginctl show-user "$USER" --property=Linger
systemctl --user daemon-reload
systemctl --user enable --now container-web.service
Юнит удобнее не писать руками, а описывать через Quadlet: файл .container в ~/.config/containers/systemd/ превращается в службу при daemon-reload. Генератор podman generate systemd устарел, и в новых версиях на него не стоит опираться. Не передавайте в ExecStart флаг --restart=always: перезапуском управляет systemd, а не Podman, иначе получите борьбу двух менеджеров.
Типичные ошибки и диагностика
cannot clone: Operation not permitted— user namespaces запрещены ядром или отсутствуетnewuidmap. Проверьтеsysctl user.max_user_namespacesи права на хелперы.newuidmap: write to uid_map failed— диапазон пересекается с другим пользователем либо с бинарников снят бит setuid.permission deniedна томе — владелец каталога не совпадает с UID внутри контейнера; помогут:Uилиpodman unshare chown.short writeи странные ошибки слоёв — кончилось место или сбойfuse-overlayfs; смотритеpodman system df.
podman unshare cat /proc/self/uid_map
ls -l /usr/bin/newuidmap /usr/bin/newgidmap
journalctl --user -u container-web.service -n 50 --no-pager
podman system prune --all --volumes
Перед очисткой проверьте, что в podman system df занимает место: часто виноваты не образы, а остановленные контейнеры и неиспользуемые тома. И помните, что --volumes удаляет данные безвозвратно.
Итог
Rootless Podman — это не флаг, а другой способ работы с ядром: user namespace, диапазоны subuid, fuse-overlayfs и rootlessport. Если подготовить /etc/subuid, включить linger и заранее решить вопрос с портами и правами на тома, ограничения перестают мешать. Вы получаете контейнеры, которые не могут незаметно переписать системные файлы, и стек, который одинаково работает на ноутбуке и на сервере без демона с правами root.