将 Caddy 改为以 UID=1000 运行,是彻底解决这种“Docker 容器间权限壁垒”的最根本方法(被称为“统一 UID 策略”)。
但这个操作绝不能简单地加一行代码就完事,因为它伴随着两个非常直接的风险。如果你不提前处理好,Caddy 启动瞬间就会崩溃。
⚠️ 风险预警(为什么要谨慎?)
风险 1:无法监听低端口(致命错误)
在 Linux 系统中,只有 root 用户有权限绑定 1024 以下的端口。
你的 Caddy 承担了 80, 443, 25, 465, 993 等大量特权端口。一旦降级为 1000 用户,Caddy 启动时会立刻报 permission denied 无法绑定端口而直接退出。
👉 解法:必须通过 Docker 给它单独追加 NET_BIND_SERVICE 内核权限。
风险 2:历史数据变“只读”(启动崩溃)
你之前以 root 运行了很久,现在的 /opt/caddy/caddy_data 目录全都是 root 的资产。
如果你突然让 1000 用户接管,Caddy 甚至连自己的日志、配置文件和已有的证书都写不进去,会立刻挂掉。
👉 解法:在重启前,必须在宿主机手动把历史资产“过户”给 1000。
🛠️ 安全操作指南(4 步无痛切换)
如果你准备好了,请严格按照以下顺序操作:
第一步:停止 Caddy 容器
不要直接重启,先让它停下来,防止我们在改权限时它正在写入数据。
Bash
docker stop caddy_edge(注:请把 caddy_edge 换成你实际的 Caddy 容器名或通过 docker compose 停止)
第二步:在宿主机“过户”文件资产
找到你 Caddy 挂载在宿主机的物理目录(主要是 data 和 config)。根据你之前的线索,执行:
Bash
# 将证书/数据目录的所有者改为 UID 1000 和 GID 1000
chown -R 1000:1000 /opt/caddy/caddy_data
# 如果你还挂载了 config 目录,也要一起改,例如:
# chown -R 1000:1000 /opt/caddy/caddy_config第三步:修改 docker-compose.yml
打开 Caddy 的配置,在服务层级追加 user 和 cap_add 两个关键配置:
YAML
services:
caddy_edge:
image: caddy:latest
# 1. 强制以 UID 1000 和 GID 1000 运行
user: "1000:1000"
# 2. 赋予绑定 1024 以下特权端口的内核能力(必须加!)
cap_add:
- NET_BIND_SERVICE
ports:
- "80:80"
- "443:443"
- "25:25"
- "465:465"
- "993:993"
# ... 保持你原来的 ports 不变 ...
volumes:
# ... 保持你原来的 volumes 不变 ...第四步:重新创建并观察日志
强制重新构建容器,让新配置生效:
Bash
docker compose up -d --force-recreate立刻查看日志:
Bash
docker logs --tail 50 caddy_edge🚀 判定胜负
如果 Caddy 日志没报错,且
8080管理页面能打开:- 恭喜!Caddy 已经成功降权运行。
以后会有什么变化?
- 以后 Caddy 每次续期申请的新证书,Owner 直接就是
1000。 - Stalwart(同样是 1000)去读这些证书时,就跟读自己家里的文件一样,再也不需要你手动
chmod 644了。一劳永逸。
- 以后 Caddy 每次续期申请的新证书,Owner 直接就是
如果在某一步报错了(比如日志里有 permission denied),那通常是因为有个别冷门挂载目录忘了 chown。