Skip to content

Advanced ExternalDNS

How the ExternalDNS provider decides what ExternalDNS may manage, how a sync is applied, and the adapter's full reference.

How grants become the domain filter

GET /external-dns/domains builds the domain filter ExternalDNS negotiates at startup from the token role's grants. A grant's name enters it only when the role holds all three of record:read, record:create, and record:delete there for a record type in common that ExternalDNS writes (A, AAAA, CNAME, or TXT), in grants whose patterns cover that name: one sync reads ownership records, adds, and deletes as one transaction, so a name missing any of them would only fail every sync it reaches. record:update is never used. A grant without --zone contributes every existing zone.

A grant narrowed to a subtree (--pattern '*.k8s') filters to that subtree rather than its zone, so ExternalDNS plans inside it. A filter entry always covers the name and everything under it, which is all ExternalDNS can express: a grant narrowed by record type, to the apex, or to one exact name reads wider there than it is, and ExternalDNS will plan changes Bindizr rejects. Keep the type list covering what your sources produce, TXT included, or ownership records (--registry=txt) fail.

What to expect

  • Record types: A, AAAA, CNAME, and TXT; anything else is rejected with a clear error, never silently dropped. Ownership TXT records (--registry=txt) are stored and returned verbatim.
  • Atomic and idempotent: one ExternalDNS sync applies as a whole — every zone in it or none — and retried requests are no-ops.
  • SOA serials: only zones with an actual change advance their serial, once per sync, with IXFR history for secondaries.
  • TTL: records without a TTL use the zone's default TTL.
  • Zone matching: the most-specific existing zone wins (api.internal.example.com → internal.example.com, never the parent).

Adapter reference

Flag Environment variable Default
--bindizr-url BINDIZR_URL required
--token BINDIZR_API_TOKEN none
--token-file BINDIZR_API_TOKEN_FILE none (takes precedence over --token)
--ca-file BINDIZR_CA_FILE none (added to the system roots; needed for a private or self-signed Bindizr certificate)
--listen-addr BINDIZR_EXTERNAL_DNS_LISTEN_ADDR 127.0.0.1:8888
--health-listen-addr BINDIZR_EXTERNAL_DNS_HEALTH_ADDR 0.0.0.0:8080
--timeout-secs BINDIZR_EXTERNAL_DNS_TIMEOUT_SECS 8 (keep under external-dns's 10s webhook write timeout)
--log-level BINDIZR_EXTERNAL_DNS_LOG_LEVEL info

The health listener serves GET /healthz (Bindizr answers and accepts this token) and GET /metrics (bindizr_external_dns_requests_total, bindizr_external_dns_request_duration_seconds).

Running standalone

If the adapter cannot live in the external-dns pod, run it as its own Deployment with --listen-addr 0.0.0.0:8888 and point --webhook-provider-url at its Service. The adapter→Bindizr hop stays authenticated, but external-dns→adapter is then plain HTTP: keep the Service ClusterIP, never expose it through an Ingress, and restrict access to the external-dns pods with a NetworkPolicy. The sidecar layout is the recommended default.