A Caddy DNS provider module for the IONOS Cloud DNS API. Enables automatic HTTPS certificates (including wildcards) via DNS-01 ACME challenges.
Important: This module uses the IONOS Cloud DNS API (
dns.de-fra.ionos.com), NOT the IONOS Hosting DNS API (api.hosting.ionos.com). These are completely different products with different authentication. If you use IONOS shared hosting, see caddy-dns/ionos instead.
- Automatic TLS certificates via DNS-01 ACME challenges (including wildcard certs)
- Full libdns implementation:
GetRecords,AppendRecords,SetRecords,DeleteRecords - Caddyfile support with block and shorthand syntax
- Environment variable substitution for secure token handling (
{$VAR},{env.VAR}) - Zone-ID caching with TTL to minimize API calls
- Configurable API endpoint for custom or regional deployments
- Structured error messages with no token leakage
- Debug logging via Caddy's standard
zap.Logger
- Your domain's NS records must point to IONOS Cloud nameservers (
ns-ic.ui-dns.de,ns-ic.ui-dns.org,ns-ic.ui-dns.biz,ns-ic.ui-dns.com). DNS-01 challenges will fail if the zone is not properly delegated. - xcaddy for building Caddy with plugins (
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest)
xcaddy build --with github.com/JoschaP/caddy-dns-ionoscloudTo pin a specific version:
xcaddy build --with github.com/JoschaP/caddy-dns-ionoscloud@v1.0.0Or use the pre-built Docker image:
docker pull ghcr.io/joschap/caddy-dns-ionoscloud:latestOr build your own:
FROM caddy:2-builder AS builder
RUN xcaddy build --with github.com/JoschaP/caddy-dns-ionoscloud
FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddySee Authentication below for a detailed step-by-step guide.
export IONOS_DNS_TOKEN="your-token-here"{$IONOS_DNS_TOKEN} is Caddy's environment variable syntax — it reads the token from the environment at startup.
*.example.com, example.com {
tls {
dns ionoscloud {$IONOS_DNS_TOKEN}
}
@www host www.example.com
handle @www {
respond "Hello from www!"
}
@api host api.example.com
handle @api {
reverse_proxy localhost:8080
}
}./caddy runThat's it. Caddy will automatically:
- Create a
_acme-challengeTXT record via the IONOS Cloud DNS API - Complete the DNS-01 challenge with Let's Encrypt
- Obtain a wildcard certificate for
*.example.com - Serve your sites over HTTPS
- Renew the certificate before it expires
Block syntax (recommended):
*.example.com {
tls {
dns ionoscloud {
api_token {$IONOS_DNS_TOKEN}
}
}
}Shorthand syntax:
*.example.com {
tls {
dns ionoscloud {$IONOS_DNS_TOKEN}
}
}| Option | Required | Description |
|---|---|---|
api_token |
Yes | IONOS Cloud API Bearer token with DNS permissions |
api_endpoint |
No | Override the API URL (default: https://dns.de-fra.ionos.com) |
{
"module": "dns.providers.ionoscloud",
"api_token": "{env.IONOS_DNS_TOKEN}"
}services:
caddy:
image: ghcr.io/joschap/caddy-dns-ionoscloud:latest
# Or build your own: build: .
ports:
- "80:80"
- "443:443"
- "443:443/udp" # HTTP/3
environment:
- IONOS_DNS_TOKEN=${IONOS_DNS_TOKEN}
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:This module requires an IONOS Cloud API token with access to your DNS zones. For security, create a dedicated sub-user that only has access to the zones it needs.
- Log in to the IONOS Cloud DCD
- Go to Management > Users & Groups
- Click Create User — give it a descriptive name (e.g.,
caddy-dns) - No special Cloud API privileges are needed — IONOS Cloud DNS is a separate product with its own access control
IONOS Cloud DNS manages access via zone sharing, not via Cloud API user privileges. This must be configured through the DCD web interface — there is no API for this.
- Go to Menu > DNS in the DCD
- Select the zone you want to manage
- Under Sharing, add the sub-user and grant access
- Repeat for each zone Caddy should manage
- Log in to the DCD as the sub-user (use a separate browser or incognito session; the sub-user logs in at dcd.ionos.com with its own credentials)
- Go to Management > Token Management
- Click Generate Token
- Copy the token — it is only shown once
Note: IONOS Cloud API tokens have a configurable TTL. For long-running Caddy instances, set a long-lived token or create a reminder to rotate it before expiry. A 401 error during certificate renewal means the token has expired.
Before going to production, always test with the Let's Encrypt staging environment to avoid rate limits:
{
acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
}
example.com {
tls {
dns ionoscloud {$IONOS_DNS_TOKEN}
}
}Once certificates are issued successfully, remove the acme_ca line to use the production Let's Encrypt servers.
| Error | Cause | Fix |
|---|---|---|
zone "example.com" not found |
Zone not in your account or sub-user has no access | Share the zone with the sub-user in DCD > DNS > Sharing |
401 Unauthorized |
Invalid or expired token | Generate a new token in DCD > Token Management |
409 record conflict |
A record with the same name+type already exists | Delete the conflicting record manually in DCD or use SetRecords |
timed out waiting for record to fully propagate |
DNS propagation delay or zone not properly delegated | Verify zone NS records point to IONOS nameservers (ns-ic.ui-dns.*) |
Enable debug logging in Caddy to see DNS API operations:
{
debug
}All log messages from this module are prefixed with ionoscloud — filter with grep ionoscloud or search for dns.providers.ionoscloud in the JSON log output.
Verify your plugin is loaded:
./caddy list-modules | grep ionoscloudTo update to a new version, rebuild Caddy with the updated plugin:
xcaddy build --with github.com/JoschaP/caddy-dns-ionoscloud@v0.2.0For Docker, rebuild your image and redeploy. Caddy stores certificates in its data volume — they persist across rebuilds.
All DNS record types supported by the IONOS Cloud DNS API: A, AAAA, CNAME, MX, TXT, SRV, CAA, NS, etc.
This module targets the IONOS Cloud DNS API at dns.de-fra.ionos.com. It uses:
GET /zones— list zonesGET /zones/{zoneId}/records— list recordsPOST /zones/{zoneId}/records— create recordPUT /zones/{zoneId}/records/{recordId}— update recordDELETE /zones/{zoneId}/records/{recordId}— delete record
Authentication is via Bearer token in the Authorization header.
See CONTRIBUTING.md for the full development guide.
- Go 1.25+
- An IONOS Cloud account with DNS zones (for integration tests)
# Unit tests only (no API access needed)
go test -v -run TestCaddyfile
# Integration tests (requires IONOS credentials)
IONOS_DNS_TOKEN="$(cat .credentials/ionos_dns_token)" \
IONOS_TEST_ZONE="$(cat .credentials/test_zone)" \
go test -vYou can store credentials in a .credentials/ directory (git-ignored):
mkdir -p .credentials
echo "your-api-token-here" > .credentials/ionos_dns_token
echo "your-test-zone.example.com" > .credentials/test_zone
chmod 600 .credentials/*Warning: Integration tests create and delete real DNS records. Always use a dedicated test zone.
git tag v1.0.0
git push origin v1.0.0This triggers the CI pipeline which runs tests and creates a GitHub Release.
MIT — see LICENSE.
- Caddy — the web server
- libdns — the DNS provider interface
- IONOS Cloud DNS API — API documentation
- caddy-dns/ionos — for IONOS Hosting DNS (different API)