服务:docker的rootful和rootless困惑

一句话结论

rootful 与 rootless 不是“模式开关”,而是两个完全独立的 docker daemon(各自一套 socket、数据目录、镜像与容器,互不共享)。维护时第一件事不是“docker 坏了吗”,而是先确认你在跟哪一个 daemon 说话 —— 本机(max2-nixos)自 2026-10-11 起为 rootless-only,rootful 已不启用。

一、两者差异对照(客观项)

对比项rootful(系统级)rootless(用户级)
daemon 进程属主root(uid 0)普通用户(本机:cat / uid 1000,由 rootlesskit 拉起)
客户端 socket 路径/var/run/docker.sock$XDG_RUNTIME_DIR/docker.sock(本机 /run/user/1000/docker.sock)
数据目录(Docker Root Dir)/var/lib/docker~/.local/share/docker(本机 /home/cat/.local/share/docker)
镜像 / 容器 / 网络 / 卷独立一套,与 rootless 互不可见独立一套,与 rootful 互不可见
systemd 单元位置系统单元:docker.service / docker.socket(/etc/systemd/system)用户单元:systemctl --user ... docker.service(本机 /etc/systemd/user/docker.service → nix store)
容器所属 cgroup/system.slice/docker-*.scope/user.slice/user-1000.slice/user@1000.service/user.slice/docker-*.scope
开机/注销行为开机即起;不依赖用户登录需 Linger=yes(本机已开)+ 用户单元 enabled(本机已开)⇒ 可开机自启;否则登录才起
绑 <1024 端口直接支持受限(需 rootlesskit 端口转发或额外配置)
使用 user namespaces不使用(容器内 root = 真实 root 映射)使用(容器内 root 只映射到普通用户)
网络实现直接使用宿主网络栈多一跳(slirp4netns / pasta),端口转发由 rootlesskit 代理
‘主机’端口在宿主上的监听进程dockerd(root)rootlesskit(普通用户)
自动清理(autoPrune)nixpkgs 选项 virtualisation.docker.autoPrune 可用无对应机制(本机实测:全机无 autoPrune、无 prune 定时器)

二、第一判据:现在到底在跟哪个 daemon 说话

① 数据目录(最直接、不受环境变量影响):
docker info --format '{{.DockerRootDir}}'
· /home/cat/.local/share/docker ⇒ rootless
· /var/lib/docker ⇒ rootful

② 客户端去哪连(环境变量决定):
echo "$DOCKER_HOST"
· unix:///run/user/1000/docker.sock 或 $XDG_RUNTIME_DIR/docker.sock ⇒ rootless
· 空(未设)⇒ 默认找 /var/run/docker.sock ⇒ 只有 rootful 在跑时才通

③ 看 daemon 进程属主:
ps -eo user,pid,args | grep -E 'dockerd|rootlesskit' | grep -v grep
· 属主 root 的 dockerd ⇒ rootful;属主是普通用户 + rootlesskit ⇒ rootless

④ 看容器归哪个 slice:
cat /proc/$(docker inspect -f '{{.State.Pid}}' 容器名)/cgroup | head -1
· /system.slice/... ⇒ rootful;/user.slice/user-1000.slice/... ⇒ rootless

三、本机现状(max2-nixos,2026-10-11 实测)

项实测值
运行中的 daemon仅 rootless(系统 docker.service / docker.socket = inactive;/etc/systemd/system 下 docker 单元 0 个)
daemon 进程rootlesskit + dockerd,属主全为 cat(uid 1000)
socket/run/user/1000/docker.sock(另有 rootful 时代残留的 /var/run/docker.sock 已于 2026-10-11 删除)
Docker Root Dir/home/cat/.local/share/docker(792 MB)
rootful 残留数据/var/lib/docker 372 MB(无任何 daemon 使用;且 docker system df 看不到它)
开机自启Linger=yes + 用户单元 enabled ⇒ 开机自动拉起 rootless
DOCKER_HOST 来源NixOS 生成于 /etc/set-environment(条件:未设且 XDG_RUNTIME_DIR 存在)
自动清理无(全机无 prune 定时器);当前 docker system df:3 镜像 827.9MB,可回收 0B
nix 侧配置modules/system/service/docker.nix:rootless.enable = true + setSocketVariable = true;另包 docker-compose + 别名 d = docker compose

四、故障 → 根因 → 对应动作(维护常用)

现象根因动作
Cannot connect to the Docker daemon at unix:///var/run/docker.sockDOCKER_HOST 未设(客户端找 rootful 的默认路径),而本机只有 rootlesssource /etc/set-environment 或重开终端;或临时 export DOCKER_HOST=unix:///run/user/1000/docker.sock
dial unix /var/run/docker.sock: no such file or directory同上,且路径本身不存在同上(此报错比上面那条更易定位)
permission denied while trying to connect to the Docker daemon socket用户在连 rootful socket 且不在 docker 组确认是否该用 rootless(本机就应用 rootless)
docker compose ps 报 no configuration file providedcompose 子命令要求当前目录有 compose 文件cd 到项目目录,或 docker compose -f <文件> ps
镜像/容器“平白消失”切过 daemon(rootful ↔ rootless),另一套数据里没有用第二节第①条判据确认数据目录;需要则重新拉取
磁盘只增不减无 autoPrune;镜像层与构建缓存累积docker system df 看可回收 → docker system prune(加 -a 会删未使用镜像,需先确认)

五、日常维护动作

看占用与可回收:
docker system df # 镜像/容器/卷/构建缓存四类
du -sh ~/.local/share/docker # rootless 数据目录真实占用
sudo du -sh /var/lib/docker # 另一套(若存在,说明跑过 rootful)

清理(按危险度递增):
docker system prune # 删停止的容器 + 悬空镜像 + 无主网络
docker system prune -a # ⚠ 连未被容器引用的镜像一起删
docker image prune # 只清镜像

看当前跑的实例(不依赖 compose 目录):
docker ps --format '{{.Names}} {{.Status}} {{.Ports}}'

看 compose 项目(需在项目目录或 -f 指定):
docker compose ps / docker compose logs -f

重启某个 daemon(会连带重启它名下所有容器,含代理类容器):
systemctl --user restart docker.service # rootless(本机在用的)
systemctl restart docker.service # rootful(本机已不启用)

六、rootless 的能力边界(以本机实例为证)

能力rootless 下表现
cap_add: NET_ADMIN可用(本机 clash 容器 CapAdd=[CAP_NET_ADMIN],运行正常)
映射 /dev/net/tun可用(本机 clash 容器 Devices=[/dev/net/tun rwm])
自定义 bridge 网络可用(本机 clash / clash-ui / mariadb 均在 ghost_net,按容器名互访正常)
容器内进程以 root 身份运行可以,但仅是 user namespace 内的 root(映射到宿主普通用户)
--privileged、真 host 网络、网关/DHCP 类容器受限或不可用(本机无此需求)
绑 <1024 端口需通过 rootlesskit 端口转发实现
多用户共享实例不支持(每用户各自一套 daemon/数据)
随 nix 世代回滚不适用:镜像/容器/数据在用户目录,与 nix 世代无关
性能网络多一跳(slirp4netns,本机 MTU 65520);本机实测 daemon 空闲 CPU≈0.02%、内存≈59MiB

七、易踩的坑(本机真实发生过的)

坑表现规避
用 sudo docker ... 起容器落到 rootful,留下一套数据与 /var/run/docker.sock(本机 372MB 就是这么来的)一律不加 sudo,用普通用户跑 docker compose up -d
残留的 rootful socket 存在但无 daemon报“Cannot connect ... Is the daemon running?”(易误判为 docker 挂了)用第二节第①条判据确认;无用则删(本机已删)
改了 compose 端口/socket 相关设置但没重建容器宿主侧监听地址仍是旧的改后 docker compose up -d 重建,再用 ss -lntp 验证
把 rootful 与 rootless 当成“同一个 docker 的两种启动方式”找不到镜像、端口被占、容器重复记住它们数据完全不互通(第一节表第 4 行)

八、相关笔记

  • 「服务:nix收敛代理配置」xi6ugMzM4rB0 —— mihomo 容器跑在本机 docker 上,代理故障的 30 秒定位手册在那里