Prefer the webhook when DNZ’s API is easier to reach from the cluster than its DNS port (behind a
reverse proxy, HTTPS only), when you’d rather hand out an API token than a TSIG key with zone transfers,
or when you want ExternalDNS’s records marked in DNZ. Prefer RFC 2136 when you’d rather not run a
sidecar and DNZ’s DNS port is reachable from the pods; see
Dynamic updates.
How the webhook runs
ExternalDNS’s webhook protocol has no authentication, so the webhook runs as a sidecar in ExternalDNS’s pod: it listens on127.0.0.1:8888 (where ExternalDNS calls by default) and holds the API
token; /healthz on port 8080 serves the pod’s probes. The sidecar is DNZ’s own image (terrificci/dnz)
started as dnz externaldns-webhook, a client of DNZ’s API like the rest of the dnz command line, so
DNZ’s server needs no setting for it.
- Zones: every primary zone the token reaches, narrowed by
DNZ_DOMAIN_FILTER. - Changes: one bulk request per zone, all or nothing.
- Ownership: ExternalDNS’s TXT registry records (
a-<name>) are stored as they are, so ExternalDNS only touches records it created. Records you made in DNZ stay yours. - Change approval: a change waiting for approval is logged with the request’s number and not sent again while it waits.
- Records in views, disabled records and ALIAS records are left out; routing policies
(
setIdentifier) aren’t supported.
Set it up
1
Create an API token
Settings → API tokens → Create token with
zones.read and records.write, limited to the zones
ExternalDNS may manage. Or:2
Store the token
3
Install ExternalDNS with Helm
external-dns-values.yaml
4
Check the webhook's log
policy: sync also deletes records whose Service or Ingress is gone; upsert-only never deletes.
txtOwnerId must differ per cluster when several clusters share a zone.
Webhook settings
Each setting is an environment variable or a flag ofdnz externaldns-webhook (--help lists them).
Outside Kubernetes the same command runs next to an ExternalDNS started any other way:
Try it on kind
A throwaway kind cluster publishing a Service into a DNZ zone. The pods only need to reachDNZ_URL (on Docker Desktop, http://host.docker.internal:8080 reaches a DNZ on the same
machine).
hello gets an A record with the Service’s IP, tagged external-dns, and a-hello the TXT that marks it
as owned by dnz-demo. Delete the Service and the next loop removes both. Clean up with
kind delete cluster --name dnz-demo.
Troubleshooting
checking DNZ_TOKEN against DNZand the pod restarts: the token is wrong or expired, orDNZ_SERVERisn’t reachable from the pod.the token reaches no zone the domain filter allows: the token is limited to other zones, or the zone is a secondary (change it on its primary).- ExternalDNS logs an error with DNZ’s message (a CNAME next to other records, a name with an ALIAS): ExternalDNS retries every loop until the conflict is gone.
change request #… waits for approval in DNZ: change approval is on for the zone; an approver accepts it under DNS → Changes or withdnz change approve <id>.