mirror of
https://github.com/coollabsio/coolify-docs.git
synced 2026-06-19 07:35:55 +00:00
202 lines
7.2 KiB
Plaintext
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.
|