目录映射与权限:坑最多的地方

容器是临时的,数据不是——这句话记住,映射这件事就有了主心骨。

新手最容易栽的跟头:在容器里跑了半个月的应用,某天删掉容器重建,配置全没了。原因很简单——容器自己的文件系统随容器删除而消失。解法也很直白:把 NAS 上的目录映射进容器,应用把数据写进这些目录,实际落在了 NAS 的硬盘上。映射出来的目录在容器删除、重建、更新镜像之后依然原样,这就是「数据持久化」。

Container Manager 里映射叫「卷」,向导里一项一项填,compose 里写成 本地路径:容器路径。两种风格效果一样,compose 更直观,因为所有映射摆在一个列表里,一目了然。

映射有两条基本要求:容器里应用要写数据的路径,必须全部映射出来;NAS 这边的目录要提前建好,并且权限给够。前者查应用的 Docker Hub 文档,后者就是本篇的重点。

bind mount 与权限模型

群晖上的做法几乎都是 bind mount,也就是把 NAS 上一个真实路径直接挂进容器,比如 /volume1/docker/jellyfin 映射到容器内的 /config。相对的还有 Docker 原生 volume,它存在一个 DSM 不太好做快照的位置,因此不推荐。bind mount 的额外好处是能用 Btrfs 快照保护这些配置目录,和 NAS 上其他数据一视同仁,备份策略也可以直接套用 Hyper Backup 对共享文件夹的任务,不必为容器单独一套。

映射方式 数据落点 快照保护 推荐度
bind mount /volume1/docker/应用名 支持 首选
原生 volume Docker 内部目录 不方便 不推荐
只读挂载 只进不出 同上 配置只读场景

权限是这里的大坑。容器里的应用通常以某个非 root 用户运行,它写 NAS 目录的前提是:NAS 上有一个 UID、GID 和容器内用户一致的用户,且这个用户对映射目录有写权限。LinuxServer 系镜像用 PUID、PGID 两个环境变量声明身份,官方镜像往往直接固定为某个 UID。

这套机制的本质是 Linux 的用户模型:容器内外的 UID 是同一套数字,名字可以不同。NAS 上的 1026 用户和容器里的 1026 用户,系统看来就是同一个人。所以不需要在容器里建同名用户,只要数字对上,读写权限自然成立。反过来,数字对不上时,所有者是 root 的目录,普通容器用户连读都费劲。

动手前的目录准备

先把目录架子搭好。打开 File Station,在 docker 共享文件夹下按应用建子文件夹,比如 /docker/jellyfin、/docker/qbittorrent。共享文件夹本身的权限建议设置为:docker 用户可读写、其他用户只读或无权限。这样容器配置文件既不会被其他套件误碰,也方便日常备份——备份范围只选 docker 一个文件夹即可。

映射进容器时,compose 里写绝对路径,从 /volume1 写起。注意区分大小写,/volume1 不能写成 /Volume1。另外 Docker Hub 文档里的路径是容器内路径,照抄即可,别把容器内路径搬到 NAS 侧。路径里有空格或者中文也要小心,绝大多数镜像对这类路径支持不好,文件夹一律用英文小写加连字符的命名,这是一劳永逸的规矩。

PUID、PGID 到底填什么

先在 NAS 上建一个专用的容器用户,比如叫 docker。它不需要管理员权限,普通用户即可,然后查它的 ID。DSM 界面看不到 UID,用 SSH 查最直接:

id 用户名

输出的 uid=1026 里的 1026 就是 PUID,gid=100 里的 100 是 PGID。把这两个值写进 compose 的环境变量,再把这个用户对 docker 目录的权限设为「可读写」,链路就通了:容器里的应用以 1026 身份写文件,NAS 上文件的归属也就是 1026,File Station 里看到的所有者是 docker 用户。如果懒得建专用用户,也可以直接用你自己的账号 ID,只是日后容器配置和你个人数据混在同一归属下,整理起来略麻烦。

权限链路自下而上

不过最常见的报错恰恰发生在这里,症状是容器反复重启、日志里一连串「拒绝访问」或者 chmod 失败。排查顺序固定三步:先确认 PUID、PGID 和 id 命令输出一致;再确认映射目录的所有者或所属组是这个用户;最后看容器是不是以 root 跑的——有些官方镜像不认 PUID,这时文件归属会变成 root,普通应用反而读不了,需要反过来调整目录归属。

目录归属的修改可以在 File Station 里做:右键文件夹、属性、权限,把所有者改成 docker 用户并应用至子文件夹。也可以 SSH 上去用 chown 一把梭,效率更高:

chown -R 1026:100 /volume1/docker/jellyfin

参数里的两个数字就是 PUID 和 PGID,-R 表示递归整个目录树。改完重启容器,报错通常就消失了。

权限对与权限错

常见应用的目录套路

每个应用的挂载路径不同,但有规律可循:配置目录几乎都叫 /config 或者 /app/data,日志和缓存单独分一个路径。把缓存和配置分开放是有好意的:缓存可以随时删,配置不能。表里是几个典型例子:

应用 容器内路径 NAS 侧建议路径 说明
Jellyfin /config /volume1/docker/jellyfin 配置库
Jellyfin /cache /volume1/docker/jellyfin/cache 转码缓存可清
MariaDB /var/lib/mysql /volume1/docker/app/db 数据库文件
qBittorrent /config /volume1/docker/qbittorrent 含下载配置
qBittorrent /downloads /volume1/video/downloads 挂映射直接可见

最后两行体现了映射的另一个价值:下载工具把 /downloads 映射到媒体文件夹后,File Station 里立刻能看到下好的文件,Video Station 扫描库也直接能扫到,全程不需要复制搬运。

只读挂载与共享目录

有些目录应用只读不写,比如只给应用看媒体文件的场景。这时可以在路径后加 :ro,防止应用误写。媒体目录也可以直接映射 /volume1/video 进影音容器,权限给读即可,媒体库的权限模型比配置目录简单,坑少很多。

多容器共享同一个目录时,注意 UID 一致性:两个应用用同一个用户身份写同一个目录才不会互相挤兑。否则今天 A 应用改的文件明天 B 应用改不动,权限对不上。相关排查手法见 命令行速查。

一个忠告:权限报错出来时,先别急着用 chmod 777。它一时管用,但会让目录对所有用户敞开,后续排查归属更乱。正确做法是让容器用户和目录归属对齐。

常见症状速查

权限和映射的问题都有固定症状,对号入座能省不少事。下表收录了社区论坛里最高频的几类提问,症状一栏对应容器列表或日志里能直接观察到的现象,处理办法给出的是第一步动作,走完还不行再回头细查:

症状 大概率原因 处理办法
容器反复重启 目录写不进去 查 PUID 与目录归属
配置恢复默认 没映射配置目录 补上 /config 映射重建
File Station 里所有者是 root 容器以 root 运行 改归属或换用户身份
升级镜像后配置丢失 映射路径变了 看新版本文档调整映射
媒体库扫不到新文件 下载目录没映射对 检查容器内外路径

养成每周扫一眼 docker 文件夹的习惯,顺手看看各应用 config 目录是不是在正常更新文件,时间戳能说明很多问题。权限这事第一次配的时候折腾半天,后面所有新容器照抄同一套 UID 就行了,算是一次付账长期受益的典型。

延伸阅读