Files
coolify-docs/content/docs/knowledge-base/proxy/caddy/dns-challenge.mdx
T

202 lines
7.2 KiB
Plaintext

---
title: "DNS Challenge"
description: "Switch Caddy from HTTP challenge to DNS challenge for ACME (Let's Encrypt) certificates — required for wildcard certs or servers without a public port 80."
---
# Switch Caddy to DNS Challenge
By default, Coolify configures Caddy to obtain SSL certificates using the **HTTP challenge**, which requires port 80 to be publicly reachable. There are two common reasons to switch to the **DNS challenge** instead:
- You want [wildcard SSL certificates](https://caddyserver.com/docs/automatic-https#wildcard-certificates) (e.g., `*.example.com`) — these *require* DNS challenge.
- Your server does not have a public port 80 (e.g., internal network, behind a firewall, or a Tailscale-only node).
## How It Works
Instead of proving domain ownership over HTTP, Caddy asks your DNS provider to create a temporary `TXT` record under `_acme-challenge.<your-domain>`. Let's Encrypt reads that record to confirm ownership, then issues the certificate.
Unlike Traefik, **Caddy DNS provider support must be compiled into the binary**. The default `lucaslorentz/caddy-docker-proxy` image does not include any DNS provider modules, so the configuration below uses a `dockerfile_inline` build to produce the correct binary automatically — no separate build step or registry required.
## Prerequisites
- A domain managed by a [supported DNS provider](https://github.com/orgs/caddy-dns/repositories?utm_source=coolify.io).
- An API token / key for that provider with permission to create and delete DNS records.
## Configuration
Go to **Servers → your server → Proxy** and apply the changes shown below to your existing Caddy configuration.
<Tabs items={['Hetzner', 'Cloudflare', 'AWS (route53)']}>
<Tab value="Hetzner">
```yaml
name: coolify-proxy
networks:
coolify:
external: true
services:
caddy:
container_name: coolify-proxy
image: 'lucaslorentz/caddy-docker-proxy:2.8-alpine' # [!code --][!code focus]
build: # [!code ++:7][!code focus:7]
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> # [!code ++][!code focus]
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} # [!code ++][!code focus]
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'
```
</Tab>
<Tab value="Cloudflare">
```yaml
name: coolify-proxy
networks:
coolify:
external: true
services:
caddy:
container_name: coolify-proxy
image: 'lucaslorentz/caddy-docker-proxy:2.8-alpine' # [!code --][!code focus]
build: # [!code ++:7][!code focus:7]
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> # [!code ++][!code focus]
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} # [!code ++][!code focus]
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'
```
</Tab>
<Tab value="AWS (route53)">
```yaml
name: coolify-proxy
networks:
coolify:
external: true
services:
caddy:
container_name: coolify-proxy
image: 'lucaslorentz/caddy-docker-proxy:2.8-alpine' # [!code --][!code focus]
build: # [!code ++:7][!code focus:7]
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> # [!code ++:3][!code focus:3]
- 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 # [!code ++][!code focus]
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'
```
</Tab>
</Tabs>
<Callout type="info" title="Note">
For other DNS providers, find the module path at [github.com/caddy-dns](https://github.com/orgs/caddy-dns/repositories?utm_source=coolify.io), replace the `--with github.com/caddy-dns/<provider>` line in the Dockerfile, and set the appropriate environment variable and `caddy.acme_dns` label.
</Callout>
Restart the proxy after saving. Caddy will build the image on first start and then use the DNS challenge to obtain and renew SSL certificates.
## Troubleshooting
**Certificate not issuing / DNS record not found**
DNS propagation can be slow. You can add an explicit delay per-site by editing `/data/coolify/proxy/caddy/dynamic/Caddyfile` on your server:
```sh
your.domain.com {
tls {
dns hetzner {env.HETZNER_API_TOKEN}
propagation_delay 30s
}
}
```
**Rate limits**
Let's Encrypt enforces [rate limits](https://letsencrypt.org/docs/rate-limits/?utm_source=coolify.io). While testing, switch to the staging CA to avoid burning your quota. Add this label to the proxy container:
```yaml
- caddy.acme_ca=https://acme-staging-v02.api.letsencrypt.org/directory
```
Remove this label once everything works.
**Wrong or missing DNS module**
If Caddy starts but certificates fail with an `unknown provider` error, the DNS module was not included in the image build. Verify the `--with github.com/caddy-dns/<provider>` line in the `dockerfile_inline` matches your provider, then restart the proxy to trigger a rebuild.