权限问题不是玄学,核心是容器进程用什么身份写宿主机目录。
\n
NAS 上跑 Docker,权限问题出现得特别频繁。页面上可能只是“初始化失败”或“无法保存设置”,日志里才会看到 permission denied、无法创建数据库、无法写配置文件。这个问题不神秘,本质是容器里的进程用某个 uid/gid 去写宿主机目录,而宿主机文件系统并不一定允许它写。
我排查权限时,不先猜镜像,也不先把目录改成所有人可写,而是先看数字身份。
id bbong9
stat /srv/docker/app/config
ls -ln /srv/docker/appid 看用户和组,stat 看目录属主和权限,ls -ln 用数字显示 uid/gid,避免被不同系统上的用户名误导。用户名可以变,数字才是内核真正判断权限时看的东西。
PUID/PGID 解决的是映射问题
LinuxServer 这类镜像常用 PUID 和 PGID,让容器内应用按宿主机指定用户运行。比如宿主机用户 uid 是 1000、gid 是 1000,就可以这样写:
services:
app:
image: lscr.io/linuxserver/example:1.0.0
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=Asia/Shanghai
volumes:
- /srv/docker/app/config:/config
- /srv/archive:/archive:ro这样应用写 /config 时,宿主机看到的文件更可能归属于你的 NAS 用户,后续用文件管理器、备份工具或 SSH 维护时不会满地 root 文件。它的好处是把容器和宿主机的写入身份对齐,减少后续权限混乱。
不是所有镜像都支持
PUID/PGID 不是 Docker 标准功能,它只是部分镜像约定出来的环境变量。官方数据库镜像、某些 Go 应用、自带用户模型的服务,可能完全不读取这两个变量。你把变量写进去,容器照样用自己的用户运行,权限问题不会因此消失。
遇到不支持 PUID/PGID 的镜像,我会看三件事:文档是否支持 user: 指定运行用户;镜像是否要求目录属主固定成某个 uid;初始化阶段是否必须用 root 修正权限。如果文档要求明确,就按文档处理,不硬套 LinuxServer 的习惯。
services:
worker:
image: example/worker:2.1.0
user: "1000:1000"
volumes:
- /srv/docker/worker/data:/datauser: 也不是万能的。有些镜像入口脚本需要 root 做初始化,强行指定普通用户会让启动失败。所以改之前先读日志和文档,不要把所有权限问题都归结为缺 PUID。
不要用 777 掩盖问题
chmod -R 777 很诱人,因为它常常能立刻让服务跑起来。但这相当于放弃了“谁能写、谁写了什么”的边界。时间久了,目录里混着多个服务写入的文件,备份和恢复都会变得危险。
更稳的做法是按服务拆目录,再把目录属主或组权限设给需要写入的用户。只读资料目录就用只读挂载,不需要为了省事给写权限。比如应用只读取 /srv/archive,Compose 里就挂成 /archive:ro;配置目录需要写,才给应用用户写权限。
我的权限排查顺序
权限报错时,我会按这个顺序看:
docker compose logs --tail=120 app
id bbong9
stat /srv/docker/app/config
ls -ln /srv/docker/app/config
docker inspect app --format '{{json .Config.User}}'
docker inspect app --format '{{json .Mounts}}'先确认应用到底写哪个路径失败,再看宿主机目录的数字属主,最后看容器声明的运行用户和挂载路径。很多问题会在 ls -ln 里露出来:目录是 root 写的,或者组权限缺失,或者容器实际挂到了另一个空目录。
权限修好以后,我还会创建一个测试文件、重启容器、再看新文件属主是否符合预期。一次启动成功不代表权限长期正确,尤其是 NAS 共享权限、ACL 和 Docker 用户同时参与时,数字检查比页面提示可靠得多。
此处评论已关闭