Rootless-режим в Docker — это запуск и демона, и контейнеров от обычного пользователя без привилегий root. На практике это один из самых простых способов уменьшить потенциальный ущерб: даже если злоумышленник «выбрался» из процесса приложения, он упирается в права непривилегированного пользователя, а не получает полный контроль над хостом.
Но rootless — не «волшебная кнопка». Он опирается на user namespace, меняет модель сети (часто через slirp4netns) и по‑другому взаимодействует с лимитами ресурсов (особенно с cgroup v2). Ниже — практичный маршрут: от требований к системе до продакшен‑схемы и диагностики.
Когда rootless Docker действительно нужен (а когда нет)
Rootless имеет смысл, если вы хотите сократить blast radius и не раздавать sudo там, где он не нужен:
- несколько проектов/клиентов на одном сервере;
- доступ разработчикам к сборке/запуску контейнеров без прав администратора;
- CI/runner на общей машине, где компрометация должна оставаться максимально «локальной»;
- модель «каждый сервис — отдельный Unix‑пользователь со своим Docker».
Rootless может не подойти, если критичны:
- максимальная производительность сети и минимальная задержка (usermode‑сеть чаще медленнее);
- кейсы с низкоуровневыми привилегиями, часть сценариев
--privileged, специфические монтирования и драйверы; - жёсткое управление ресурсами через cgroup v1 (в rootless проще жить на cgroup v2).
Как это работает: user namespace и «root внутри»
Ключевой механизм rootless Docker — user namespace. Он позволяет процессу быть «root» внутри своего пространства имён, но соответствовать непривилегированному UID на хосте.
Упрощённо: внутри контейнера вы видите UID 0, а снаружи это ваш UID (например, 1000) и/или диапазон «подчинённых» UID (subuid). Поэтому попытки сделать что‑то действительно привилегированное на хосте не сработают: ядро проверяет права хостового пользователя.
Rootless повышает устойчивость к последствиям взлома контейнера, но не отменяет необходимость обновлений, минимизации образов, секретов и сетевых ограничений.
Что нужно ядру и системе
Перед началом проверьте базу:
- непривилегированные user namespace разрешены в системе;
- для пользователя настроены subuid/subgid диапазоны;
- есть инструменты для rootless‑сети (обычно
slirp4netns) и утилиты маппинга UID (uidmap).

Подготовка пользователя: subuid/subgid и лимиты
Rootless Docker требует диапазоны подчинённых UID/GID, чтобы корректно маппировать пользователей внутри контейнеров на хост. Обычно это настраивается через /etc/subuid и /etc/subgid.
Проверьте, что у пользователя есть диапазон:
grep -E '^myuser:' /etc/subuid /etc/subgid
Типичная строка выглядит так:
myuser:100000:65536
Если строк нет, добавьте диапазоны (нужны права root):
usermod --add-subuids 100000-165535 --add-subgids 100000-165535 myuser
После этого перелогиньтесь пользователем (важно для окружения и корректной user‑сессии systemd).
Полезные лимиты для rootless окружения
Rootless‑сценарии часто упираются в число процессов, файловых дескрипторов и ограничения user slice. Быстро проверьте:
ulimit -n
ulimit -u
И состояние user‑сессии:
loginctl show-user myuser
Установка и запуск Docker в rootless-режиме
Важная идея: для rootless критично не то, «как поставить docker-ce», а то, как развернуть rootless‑демон под пользователем штатным установщиком.
Под целевым пользователем выполните:
dockerd-rootless-setuptool.sh install
Если скрипт не найден, чаще всего не установлен компонент rootless extras (названия пакетов зависят от дистрибутива). Удобнее всего поставить его из репозитория вашей системы, чтобы получались обновления безопасности.
Запуск rootless-демона через systemd --user
Старт и автозапуск:
systemctl --user enable --now docker
Проверка статуса:
systemctl --user status docker
Чтобы Docker CLI ходил именно в rootless‑демон (особенно в CI или non‑login shell), часто помогает явное указание сокета:
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
Проверка:
docker context ls
docker info
Чтобы сервис не умирал после выхода из SSH
Rootless Docker живёт в user systemd. Если сервисы падают после logout, включите linger для пользователя (нужно сделать от root):
loginctl enable-linger myuser
Сеть в rootless Docker: slirp4netns, порт-маппинг и ограничения
В rootless‑режиме Docker не может так же свободно управлять хостовым network namespace и firewall, как rootful‑демон. Поэтому типичный вариант — usermode networking через slirp4netns (часто в связке с rootlesskit).
Чего ожидать:
- контейнеры получают NAT‑подобную сеть без прав root;
- порт‑маппинг работает, но реализация отличается от классического bridge;
- производительность и latency могут быть хуже, чем у rootful bridge;
- возможны нюансы с доступом к сервисам на хосте и hairpin‑сценариями.
Публикация портов: почему 80/443 «не биндятся»
Rootless Docker не может слушать привилегированные порты (<1024), потому что процесс демона — не root. Практичные варианты:
Использовать высокие порты (например, 8080/8443) и отдавать наружу через системный reverse proxy (nginx/HAProxy), который слушает 80/443.
Разрешить bind на низкие порты для конкретного бинарника через capabilities. Делайте это только если понимаете риски и зачем вам это нужно.
Держать reverse proxy границей: root остаётся только у фронтового веб‑сервера, а приложения живут в rootless‑контейнерах.
Если вы строите типичный веб‑хостинг, связка «reverse proxy + rootless контейнеры» обычно самая предсказуемая: TLS и низкие порты остаются в системном сервисе, а прикладной код работает с минимальными привилегиями. Для внешнего TLS удобно использовать SSL-сертификаты и терминировать HTTPS на прокси.
Как понять, что реально используется slirp4netns
Посмотрите вывод:
docker info | sed -n '1,160p'
Также полезно проверить процессы rootless‑сети:
ps aux | grep -E 'slirp4netns|rootlesskit' | grep -v grep
Если вы одновременно приводите в порядок сетевую политику хоста (nftables/iptables) и поведение Docker, пригодится разбор отличий в статье про Docker и firewall: iptables vs nftables и типовые ловушки.

cgroup v2 в rootless Docker: лимиты CPU/RAM и почему «не применяются»
cgroup v2 — ключевая тема для продакшена. В rootful Docker демон управляет cgroup напрямую. В rootless ограничения ресурсов зависят от того, может ли systemd делегировать контроллеры в user slice, и как настроена система.
Проверьте, что у вас cgroup v2:
stat -fc %T /sys/fs/cgroup
Если видите cgroup2fs, это v2.
Проверка делегирования контроллеров systemd
Проверьте делегирование и подсказки в выводе Docker:
systemctl --user show --property=Delegate
docker info | grep -E 'Cgroup|cgroup'
Симптомы, что лимиты «не работают»:
- параметры
--memory,--cpusпринимаются, но контейнер реально не ограничивается; - в логах демона есть жалобы на невозможность выставить лимиты;
- контейнеры оказываются в cgroup без нужных контроллеров.
Для диагностики конкретного контейнера посмотрите PID и cgroup‑путь:
docker inspect --format '{{.Id}} {{.HostConfig.CgroupParent}}' CONTAINER
cat /proc/$(docker inspect --format '{{.State.Pid}}' CONTAINER)/cgroup
Если вы глубже используете systemd‑срезы, лимиты и делегирование, полезно свериться с практикой из материала systemd и cgroups: slices, ограничения и типовые настройки.
Практика: как тестировать лимиты
CPU:
docker run --rm --cpus=0.5 alpine sh -c 'apk add --no-cache stress-ng >/dev/null 2>&1; stress-ng --cpu 1 --timeout 20s'
Память:
docker run --rm --memory=256m --memory-swap=256m alpine sh -c 'apk add --no-cache stress-ng >/dev/null 2>&1; stress-ng --vm 1 --vm-bytes 400m --timeout 20s'
Если контейнер стабильно «уходит» за лимит, значит ограничения не применились, и нужно разбираться с делегированием контроллеров и cgroup v2.
Хранилище и права: где rootless чаще всего «стреляет»
Из-за маппинга UID/GID через user namespace меняются привычные ожидания по правам на файлы. Частые кейсы:
- bind‑mount директории с хоста: внутри контейнера файлы выглядят «не тем владельцем»;
- приложение пишет в volume, а на хосте владельцы оказываются в диапазоне subuid;
- миграция с rootful на rootless ломает права на существующих данных.
Практичный подход: не пытайтесь «подогнать» всё под UID 0. Лучше запускать сервисы от явного пользователя внутри контейнера (например, 1000:1000), и выравнивать права на данных под этот сценарий. Если это тяжело, предпочитайте именованные volumes вместо bind‑mount для чувствительных путей или добавляйте инициализацию прав на старте (entrypoint/init‑скрипт).
Rootless в продакшене: типовая схема деплоя
Для большинства веб‑проектов хорошо работает схема:
- снаружи — системный reverse proxy на 80/443, он же терминирует TLS;
- приложения и воркеры — rootless Docker под отдельным пользователем;
- между ними — публикация портов на
127.0.0.1(или на отдельный loopback‑адрес); - логирование — journald или
json-fileс ротацией, чтобы не раздувать диск.
Так вы сохраняете минимальные привилегии там, где риск выше (прикладной код), и оставляете root только для сетевой части (низкие порты, TLS, firewall). На практике такая схема отлично ложится на отдельный сервер или VDS, где приложения можно разнести по пользователям и ресурсам.
Частые проблемы и быстрая диагностика
Docker CLI «не видит» rootless-демон
Проверьте сокет и переменную окружения:
echo $DOCKER_HOST
ls -la /run/user/$(id -u)/docker.sock
docker info
Не стартует из-под SSH или падает после logout
Проверьте, что сервис запущен как user‑юнит, и при необходимости включён linger (команда выполняется от root):
systemctl --user status docker
loginctl show-user myuser | sed -n '1,120p'
loginctl enable-linger myuser
Порты не пробрасываются или не доступны снаружи
Уточните, на какой адрес опубликован порт (часто только 127.0.0.1):
docker ps
ss -lntp | grep -E 'docker|rootless|slirp|kit' || true
Для продакшена обычно проще и безопаснее публиковать сервисы на localhost и отдавать наружу через системный reverse proxy.
Лимиты ресурсов «игнорируются»
Сверьте cgroup v2, делегирование и путь cgroup контейнера. Если это виртуальная среда, дополнительно убедитесь, что хостовая система инициализирована на cgroup v2 и не режет контроллеры нестандартными политиками.
Мини-чеклист перед боевым запуском
- Есть subuid/subgid диапазон у пользователя.
- Rootless‑демон запускается через
systemctl --userи переживает logout (linger включён при необходимости). - Сеть понятна: вы знаете, используете ли
slirp4netns, и принимаете её ограничения. - Определена стратегия портов: reverse proxy на 80/443 или осознанная настройка bind low ports.
- Проверены лимиты CPU/RAM на cgroup v2 и поведение при OOM.
- Продуманы права на данные (volumes/bind‑mount) и миграция с rootful.
Итоги
Docker rootless — практичный компромисс между удобством Docker и более строгой моделью привилегий. Он особенно полезен для многопользовательских серверов, CI и ситуаций, когда выдавать sudoslirp4netns, зависимость от корректной настройки user namespace и нюансы применения лимитов на cgroup v2.
Если внедрять rootless как инженерную задачу (проверки, тесты лимитов, понятная схема портов, аккуратная работа с правами), он становится рабочей продакшен‑опцией для большого класса веб‑сервисов.


