Forest
容器化 Caddy 代理部署实战

容器化 Caddy 代理部署实战


背景

今天在维护服务器时发现,之前用 docker-compose 部署的 Caddy 反向代理服务,其配置文件所在的工作目录不知何时被删除了。虽然容器还在正常运行(因为 bind mount 的文件在容器启动后会被加载到内存中),但一旦容器重启就会因为找不到配置文件而启动失败。


问题发现与排查

第一步:查看容器状态

首先查看运行中的 Caddy 容器:

docker ps --filter "name=caddy"

发现容器确实在运行,已经运行了 4 周,镜像为 caddy:2-alpine

第二步:查看挂载配置

docker inspect caddy --format '{{json .Mounts}}' | python3 -m json.tool

输出显示:

{
    "Type": "bind",
    "Source": "/root/xxx/Caddyfile",
    "Destination": "/etc/caddy/Caddyfile",
    "Mode": "ro",
    "RW": false
}

第三步:验证宿主机文件是否存在

ls -la /root/xxx/Caddyfile

果然,文件不存在了!

第四步:查看容器内当前生效的配置

还好容器还在运行,可以直接从容器内获取当前配置:

docker exec caddy cat /etc/caddy/Caddyfile

成功获取了当前正在运行的配置内容,这为后续重建提供了基础。


踩坑记录

坑一:Bind Mount 只读挂载无法直接修改

因为配置文件是以 bind mount (只读) 方式挂载到容器的,所以不能直接用 docker cp 覆盖:

docker cp /tmp/Caddyfile caddy:/etc/caddy/Caddyfile

报错:

Error response from daemon: mount /root/.../Caddyfile:... not a directory

也不能直接在容器内写入:

docker exec caddy sh -c 'cat > /etc/caddy/Caddyfile << "EOF" ...'

报错:

sh: can't create /etc/caddy/Caddyfile: Read-only file system

解决方案:必须删除旧容器,用新的配置路径重新创建容器。

坑二:Caddyfile 语法错误

Caddy 2 的 auto_https 配置语法需要注意。一开始我写的是:

{
    auto_https on
}

结果报错:

Error: adapting config using caddyfile: parsing caddyfile tokens for 'auto_https': auto_https must be one of 'off', 'disable_redirects', 'disable_certs', or 'ignore_loaded_certs'

解决方案:Caddy 默认就是启用自动 HTTPS 的,不需要显式配置 auto_https on。删除这一行即可。

实际上,Caddy 2 的 auto_https 有效值只有:

  • off - 完全禁用自动 HTTPS
  • disable_redirects - 禁用 HTTP 到 HTTPS 重定向
  • disable_certs - 禁用自动证书获取
  • ignore_loaded_certs - 忽略已加载的证书

坑三:HTTP 到 HTTPS 重定向的自动处理

一开始我想显式配置 HTTP 重定向,但实际上 Caddy 默认就会自动将所有 HTTP 请求(80端口)重定向到 HTTPS(443端口),返回 308 Permanent Redirect。

如果显式配置了 :80 站点,反而会覆盖默认的重定向行为。


最终解决方案

第一步:创建新的配置目录和文件

在可靠的位置创建配置目录:

mkdir -p /root/caddy-config

第二步:编写新的 Caddyfile

# Matrix 服务
xxx.xxx.xx {
    reverse_proxy conduit:6167
    
    log {
        output stdout
    }
}

# Obsidian 笔记服务 (FNS)
xxx.xxx.xx {
    reverse_proxy 172.19.0.1:9000
    
    log {
        output stdout
    }
}

# HTTP 默认重定向到 HTTPS(Caddy 默认已启用)
:80 {
    respond "Welcome - Please use HTTPS" 200
}

# 默认 HTTPS 站点(未匹配的域名)
https:// {
    respond "Welcome to LiuForest Services" 200
}

主要变更

  1. ✅ 保留 Matrix 服务反向代理
  2. ✅ 保留 Obsidian FNS 笔记服务反向代理
  3. ✅ 启用所有域名的自动 HTTPS 证书(Caddy 默认行为)
  4. ✅ HTTP 自动重定向到 HTTPS

第三步:停止并删除旧容器

docker stop caddy && docker rm caddy

第四步:用新配置启动容器

docker run -d   --name caddy   --network service_network   -p 80:80 -p 443:443 -p 443:443/udp   -v /root/caddy-config/Caddyfile:/etc/caddy/Caddyfile   -v service_caddy-config:/config   -v service_caddy-data:/data   --restart unless-stopped   caddy:2-alpine

关键参数说明

  • --network service_network - 加入原有的网络,能够通过容器名(如 conduit)访问 Matrix 服务
  • -p 443:443/udp - 启用 HTTP/3 (QUIC) 支持
  • -v service_caddy-config:/config - 保留原有的配置卷(包含证书缓存)
  • -v service_caddy-data:/data - 保留原有的数据卷
  • --restart unless-stopped - 设置自动重启

第五步:验证 HTTPS 证书获取

查看容器日志,确认 Caddy 正在自动获取证书:

docker logs caddy -f

三个域名的 HTTPS 证书都自动获取成功了!

第六步:验证所有服务

验证 HTTP 到 HTTPS 重定向:

curl -s -I http://xx.xxx.xx

返回:

HTTP/1.1 308 Permanent Redirect
Location: https://xx.xxx.xx/
Server: Caddy

完美!


最终架构

现在的服务架构:

                          ┌─────────────────────────┐
                          │   Caddy 反向代理 (443)   │
                          └──────────┬──────────────┘

            ┌────────────────────────┼────────────────────────┐
            │                        │                        │
┌───────────▼──────────┐  ┌─────────▼───────────┐  ┌─────────▼───────────┐
│    xx.xxx.xx         │  │      xx.xxx.xx      │  │     xx.xxx.xx       │
│      聊天服务         │  │       笔记服务       │  │     Agent服务       │
│ conduit:6167         │  │  172.19.0.1:9000    │  │  172.19.0.1:8648    │
└──────────────────────┘  └─────────────────────┘  └─────────────────────┘

经验总结

1. 重要配置文件的存放位置

  • 不要把配置文件放在临时目录或可能被清理的目录中
  • 建议专门建立 /root/docker-configs/[服务名]/ 目录存放配置
  • 配置文件一定要备份!

2. Caddy 最佳实践

  • 信任 Caddy 的默认行为(自动 HTTPS、自动重定向),不要过度配置
  • 保留 /config/data 卷,证书会自动续期,不用手动管理
  • 使用 docker logs 查看证书获取过程,非常详细

3. Docker 容器重建注意事项

  • 重建前一定要先查看 docker inspect 的网络和卷配置
  • 确保加入正确的网络,否则容器间无法通过主机名通信
  • 保留数据卷,不要轻易删除 named volume

4. 排障思路

  1. 先看日志 - docker logs [容器名]
  2. 检查网络连通性 - docker exec [容器名] ping [目标]
  3. 验证 DNS 解析 - docker exec [容器名] nslookup [域名]
  4. 从简单到复杂,逐步验证

配置文件位置备忘

项目路径
Caddyfile 宿主机/root/caddy-config/Caddyfile
Caddyfile 容器内/etc/caddy/Caddyfile
配置卷 (证书缓存)/var/lib/docker/volumes/service_caddy-config/_data
数据卷/var/lib/docker/volumes/service_caddy-data/_data
容器日志docker logs caddy

后续维护命令

# 查看容器状态
docker ps

# 查看日志
docker logs caddy -f

# 修改配置后重新加载
docker exec caddy caddy reload --config /etc/caddy/Caddyfile

# 验证配置文件语法
docker exec caddy caddy fmt /etc/caddy/Caddyfile

# 重启容器
docker restart caddy