将 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

🚀 判定胜负

  1. 如果 Caddy 日志没报错,且 8080 管理页面能打开:

    • 恭喜!Caddy 已经成功降权运行。
  2. 以后会有什么变化?

    • 以后 Caddy 每次续期申请的新证书,Owner 直接就是 1000。
    • Stalwart(同样是 1000)去读这些证书时,就跟读自己家里的文件一样,再也不需要你手动 chmod 644 了。一劳永逸。

如果在某一步报错了(比如日志里有 permission denied),那通常是因为有个别冷门挂载目录忘了 chown。