# ExternalDNS DNS synchronization (registers/deletes records via the PowerDNS REST API and external cloud DNS APIs where applicable). Per-host-cluster infrastructure (see [`docs/PLATFORM-TECH-STACK.md`](../../docs/PLATFORM-TECH-STACK.md) §3.1) — runs on every host cluster, primarily on the DMZ block. PowerDNS (see [`docs/PLATFORM-POWERDNS.md`](../../docs/PLATFORM-POWERDNS.md)) is the authoritative server for every Sovereign zone; ExternalDNS uses the `webhook` provider (`external-dns-pdns`) to write A/AAAA/CNAME records into PowerDNS. Health-checked geo-failover lives in PowerDNS lua-records — see [`docs/MULTI-REGION-DNS.md`](../../docs/MULTI-REGION-DNS.md). **Status:** Accepted | **Updated:** 2026-04-27 --- ## Overview ExternalDNS synchronizes Kubernetes resources (Gateway, Service, Ingress) with external DNS providers, enabling automatic DNS record management. --- ## Architecture ```mermaid flowchart TB subgraph K8s["Kubernetes"] GW[Gateway API] Svc[Services] ExtDNS[ExternalDNS] end subgraph DNS["DNS Providers"] PDNS[PowerDNS
(authoritative — every Sovereign zone)] CF[Cloudflare] R53[Route53] HDNS[Hetzner DNS] end GW --> ExtDNS Svc --> ExtDNS ExtDNS --> PDNS ExtDNS --> CF ExtDNS --> R53 ExtDNS --> HDNS ``` --- ## Supported DNS Providers | Provider | Availability | |----------|--------------| | Cloudflare | Always | | Hetzner DNS | If Hetzner chosen | | AWS Route53 | If AWS chosen | | GCP Cloud DNS | If GCP chosen | | Azure DNS | If Azure chosen | --- ## Configuration ### ExternalDNS Deployment ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: external-dns namespace: external-dns spec: template: spec: containers: - name: external-dns image: registry.k8s.io/external-dns/external-dns:v0.14.0 args: - --source=gateway-httproute - --source=gateway-grpcroute - --source=service - --provider=cloudflare - --cloudflare-proxied - --txt-owner-id=openova - --txt-prefix=_externaldns. env: - name: CF_API_TOKEN valueFrom: secretKeyRef: name: cloudflare-credentials key: api-token ``` --- ## PowerDNS Integration (geo + health-checked failover) ExternalDNS writes plain A/AAAA/CNAME records into PowerDNS via the REST API. Geo-aware and health-checked failover responses are owned by PowerDNS lua-records, written by the `catalyst-dns` controller — ExternalDNS does NOT manage lua-record content. ```mermaid flowchart LR subgraph Region1["Region 1"] App1[Application] ExtDNS1[ExternalDNS] end subgraph Region2["Region 2"] App2[Application] ExtDNS2[ExternalDNS] end subgraph PDNS["PowerDNS Authoritative"] ZoneAPI[REST API] Lua[lua-records (ifurlup, pickclosest)] end ExtDNS1 -->|"plain A/AAAA"| ZoneAPI ExtDNS2 -->|"plain A/AAAA"| ZoneAPI ZoneAPI --- Lua ``` See [`docs/MULTI-REGION-DNS.md`](../../docs/MULTI-REGION-DNS.md) for the lua-record patterns. --- ## Record Types | Source | Record Type | Example | |--------|-------------|---------| | Gateway | A/CNAME | `api.` | | Service (LoadBalancer) | A | `svc.` | | catalyst-dns (lua-record author) | LUA A | `app.` (geo + health-checked) | --- ## TXT Registry ExternalDNS uses TXT records to track ownership: ``` _externaldns.api. TXT "heritage=external-dns,external-dns/owner=openova" ``` This prevents ExternalDNS from modifying records it doesn't own. --- *Part of [OpenOva](https://openova.io)*