Coolify logoCoolify

将 Caddy 切换为 DNS 挑战

默认情况下,Coolify 配置 Caddy 使用 HTTP 挑战 获取 SSL 证书,这需要 80 端口对外公开可达。通常有两个原因需要切换为 DNS 挑战

  • 您需要 通配符 SSL 证书(例如 *.example.com)——这 必须 使用 DNS 挑战。
  • 您的服务器没有公开的 80 端口(例如:内网环境、位于防火墙后方,或仅使用 Tailscale 的节点)。

工作原理

Caddy 不再通过 HTTP 验证域名所有权,而是要求您的 DNS 提供商在 _acme-challenge.<your-domain> 下创建一个临时的 TXT 记录。Let's Encrypt 会读取该记录以确认所有权,随后颁发证书。

与 Traefik 不同,Caddy 的 DNS 提供商支持必须编译到二进制文件中。默认的 lucaslorentz/caddy-docker-proxy 镜像不包含任何 DNS 提供商模块,因此下面的配置使用 dockerfile_inline 构建来自动生成正确的二进制文件——无需单独的构建步骤或镜像仓库。

前置条件

  • 受支持的 DNS 提供商 管理的域名。
  • 该提供商的 API Token / 密钥,且具备创建和删除 DNS 记录的权限。

配置

前往 服务器 → 您的服务器 → 代理,并将以下更改应用到您现有的 Caddy 配置中。

name: coolify-proxy
networks:
  coolify:
    external: true
services:
  caddy:
    container_name: coolify-proxy
    image: 'lucaslorentz/caddy-docker-proxy:2.8-alpine'
    build: 
      dockerfile_inline: | 
        FROM caddy:2.11-builder AS builder
        RUN xcaddy build --with github.com/lucaslorentz/caddy-docker-proxy/v2@v2.8.11 --with github.com/caddy-dns/hetzner/v2@v2.0.0
        FROM caddy:2.11-alpine
        COPY --from=builder /usr/bin/caddy /usr/bin/caddy
        CMD ["caddy", "docker-proxy"]
    restart: unless-stopped
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    environment:
      - CADDY_DOCKER_POLLING_INTERVAL=5s
      - CADDY_DOCKER_CADDYFILE_PATH=/dynamic/Caddyfile
      - HETZNER_API_TOKEN=<Hetzner API Token>
    networks:
      - coolify
    ports:
      - '80:80'
      - '443:443'
      - '443:443/udp'
    labels:
      - coolify.managed=true
      - coolify.proxy=true
      - caddy.acme_dns=hetzner {env.HETZNER_API_TOKEN}
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock:ro'
      - '/data/coolify/proxy/caddy/dynamic:/dynamic'
      - '/data/coolify/proxy/caddy/config:/config'
      - '/data/coolify/proxy/caddy/data:/data'
name: coolify-proxy
networks:
  coolify:
    external: true
services:
  caddy:
    container_name: coolify-proxy
    image: 'lucaslorentz/caddy-docker-proxy:2.8-alpine'
    build: 
      dockerfile_inline: |
        FROM caddy:2.11-builder AS builder
        RUN xcaddy build --with github.com/lucaslorentz/caddy-docker-proxy/v2@v2.8.11 --with github.com/caddy-dns/cloudflare@v0.2.4
        FROM caddy:2.11-alpine
        COPY --from=builder /usr/bin/caddy /usr/bin/caddy
        CMD ["caddy", "docker-proxy"]
    restart: unless-stopped
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    environment:
      - CADDY_DOCKER_POLLING_INTERVAL=5s
      - CADDY_DOCKER_CADDYFILE_PATH=/dynamic/Caddyfile
      - CF_API_TOKEN=<Cloudflare API Token>
    networks:
      - coolify
    ports:
      - '80:80'
      - '443:443'
      - '443:443/udp'
    labels:
      - coolify.managed=true
      - coolify.proxy=true
      - caddy.acme_dns=cloudflare {env.CF_API_TOKEN}
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock:ro'
      - '/data/coolify/proxy/caddy/dynamic:/dynamic'
      - '/data/coolify/proxy/caddy/config:/config'
      - '/data/coolify/proxy/caddy/data:/data'
name: coolify-proxy
networks:
  coolify:
    external: true
services:
  caddy:
    container_name: coolify-proxy
    image: 'lucaslorentz/caddy-docker-proxy:2.8-alpine'
    build: 
      dockerfile_inline: |
        FROM caddy:2.11-builder AS builder
        RUN xcaddy build --with github.com/lucaslorentz/caddy-docker-proxy/v2@v2.8.11 --with github.com/caddy-dns/route53@v1.6.2
        FROM caddy:2.11-alpine
        COPY --from=builder /usr/bin/caddy /usr/bin/caddy
        CMD ["caddy", "docker-proxy"]
    restart: unless-stopped
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    environment:
      - CADDY_DOCKER_POLLING_INTERVAL=5s
      - CADDY_DOCKER_CADDYFILE_PATH=/dynamic/Caddyfile
      - AWS_ACCESS_KEY_ID=<Access Key ID>
      - AWS_SECRET_ACCESS_KEY=<Secret Access Key>
      - AWS_REGION=<Region>
    networks:
      - coolify
    ports:
      - '80:80'
      - '443:443'
      - '443:443/udp'
    labels:
      - coolify.managed=true
      - coolify.proxy=true
      - caddy.acme_dns=route53
    volumes:
      - '/var/run/docker.sock:/var/run/docker.sock:ro'
      - '/data/coolify/proxy/caddy/dynamic:/dynamic'
      - '/data/coolify/proxy/caddy/config:/config'
      - '/data/coolify/proxy/caddy/data:/data'

注意

对于其他 DNS 提供商,请在 github.com/caddy-dns 查找模块路径,替换 Dockerfile 中的 --with github.com/caddy-dns/<provider> 行,并设置相应的环境变量和 caddy.acme_dns 标签。

保存后重启代理。Caddy 将在首次启动时构建镜像,随后使用 DNS 挑战获取并续期 SSL 证书。

故障排除

证书未签发 / 未找到 DNS 记录

DNS 传播可能需要较长时间。您可以通过编辑服务器上的 /data/coolify/proxy/caddy/dynamic/Caddyfile 为每个站点添加显式延迟:

your.domain.com {
    tls {
        dns hetzner {env.HETZNER_API_TOKEN}
        propagation_delay 30s
    }
}

速率限制

Let's Encrypt 会执行 速率限制。在测试期间,请切换到暂存 CA 以避免耗尽配额。将以下标签添加到代理容器中:

- caddy.acme_ca=https://acme-staging-v02.api.letsencrypt.org/directory

一切正常后,请移除此标签。

DNS 模块错误或缺失

如果 Caddy 启动成功但证书签发失败并提示 unknown provider 错误,说明 DNS 模块未包含在镜像构建中。请检查 dockerfile_inline 中的 --with github.com/caddy-dns/<provider> 行是否与您的提供商匹配,然后重启代理以触发重新构建。

On this page