DNS Architecture and External DNS
Relevant source files
- kubernetes/apps/cert-manager/cert-manager/app/helmrelease.yaml
- kubernetes/apps/default/freshrss/app/helmrelease.yaml
- kubernetes/apps/kube-system/coredns/app/helmrelease.yaml
- kubernetes/apps/kube-system/coredns/app/kustomization.yaml
- kubernetes/apps/kube-system/coredns/app/ocirepository.yaml
- kubernetes/apps/kube-system/metrics-server/app/helmrelease.yaml
- kubernetes/apps/kube-system/reloader/app/helmrelease.yaml
- kubernetes/apps/network/cloudflare-tunnel/app/dnsendpoint.yaml
- kubernetes/apps/network/cloudflare-tunnel/app/externalsecret.yaml
- kubernetes/apps/network/cloudflare-tunnel/app/helmrelease.yaml
- kubernetes/apps/network/cloudflare-tunnel/app/kustomization.yaml
- kubernetes/apps/network/cloudflare-tunnel/app/resources/config.yaml
- kubernetes/apps/network/cloudflare-tunnel/ks.yaml
- kubernetes/apps/network/external-dns/adguard-home/externalsecret.yaml
- kubernetes/apps/network/external-dns/adguard-home/helmrelease.yaml
- kubernetes/apps/network/external-dns/adguard-home/kustomization.yaml
- kubernetes/apps/network/external-dns/app/externalsecret.yaml
- kubernetes/apps/network/external-dns/app/helmrelease.yaml
- kubernetes/apps/network/external-dns/app/kustomization.yaml
- kubernetes/apps/network/external-dns/app/prometheusrule.yaml
- kubernetes/apps/network/external-dns/ks.yaml
- kubernetes/apps/network/external-dns/unbound/externalsecret.yaml
- kubernetes/apps/network/external-dns/unbound/helmrelease.yaml
- kubernetes/apps/network/external-dns/unbound/kustomization.yaml
- scripts/migrate_to_oci.py
This page details the split-brain DNS design implemented in the home-ops cluster. The architecture leverages three distinct external-dns instances to manage public, local override, and internal infrastructure records. It further describes the integration of Cloudflare Tunnels for secure ingress and the role of DNSEndpoint custom resources in automating record management.
Split-Brain DNS Design
The cluster utilizes a “split-brain” approach where different DNS providers handle the same domain (cloudjur.com) depending on the network context (external vs. internal). This is managed by three separate HelmRelease instances of external-dnskubernetes/apps/network/external-dns/ks.yaml1-74
DNS Provider Matrix
| Instance Name | Provider / Target | Purpose | Key Source Types |
|---|---|---|---|
external-dns | Cloudflare | Public DNS records with proxying enabled. | crd, gateway-httproute |
external-dns-adguard-home | AdGuard Home (Webhook) | Local DNS overrides for internal LAN clients. | gateway-httproute, service |
external-dns-opnsense-unbound | OPNsense Unbound (Webhook) | Internal infrastructure records for node-level resolution. | gateway-httproute, service |
Data Flow: DNS Record Lifecycle
The following diagram illustrates how a single HTTPRoute or DNSEndpoint resource triggers updates across different DNS providers.
DNS Reconciliation Flow
[Flowchart Diagram]
Sources: kubernetes/apps/network/external-dns/app/helmrelease.yaml31-42kubernetes/apps/network/external-dns/adguard-home/helmrelease.yaml65-78kubernetes/apps/network/external-dns/unbound/helmrelease.yaml75-96
External DNS Instances
1. Public Cloudflare Instance
The primary external-dns instance manages public-facing records. It is configured with --cloudflare-proxied to ensure traffic flows through Cloudflare’s edge kubernetes/apps/network/external-dns/app/helmrelease.yaml33 It specifically watches for Gateway resources labeled for envoy-externalkubernetes/apps/network/external-dns/app/helmrelease.yaml36
2. AdGuard Home Override
For local clients within the home network, external-dns-adguard-home provides DNS overrides. It uses a webhook provider (ghcr.io/muhlba91/external-dns-provider-adguard) to communicate with the AdGuard Home API kubernetes/apps/network/external-dns/adguard-home/helmrelease.yaml23-27 This allows local devices to resolve cluster services directly to internal IPs, bypassing the Cloudflare Tunnel for reduced latency.
3. OPNsense Unbound Integration
The external-dns-opnsense-unbound instance targets the Unbound DNS service running on the OPNsense router. It uses a specialized webhook (ghcr.io/crutonjohn/external-dns-opnsense-webhook) kubernetes/apps/network/external-dns/unbound/helmrelease.yaml26 and targets the envoy-internal gateway kubernetes/apps/network/external-dns/unbound/helmrelease.yaml83
Cloudflare Tunnel and Ingress
The cluster uses cloudflared to establish a secure, outbound-only connection to Cloudflare, eliminating the need for open inbound firewall ports.
Tunnel Configuration
The cloudflare-tunnel deployment runs the cloudflared binary kubernetes/apps/network/cloudflare-tunnel/app/helmrelease.yaml27-28 Its configuration routes traffic for cloudjur.com and its subdomains to the internal envoy-external service kubernetes/apps/network/cloudflare-tunnel/app/resources/config.yaml2-11
Ingress Architecture
[Flowchart Diagram]
Sources: kubernetes/apps/network/cloudflare-tunnel/app/resources/config.yaml1-12kubernetes/apps/network/cloudflare-tunnel/app/helmrelease.yaml34
CoreDNS and Internal Resolution
Within the cluster, coredns serves as the primary resolver for cluster.local domains kubernetes/apps/kube-system/coredns/app/helmrelease.yaml48-49 It is configured with the kubernetes plugin to provide service discovery and forward to upstream resolvers (typically the node’s /etc/resolv.conf) for non-cluster queries kubernetes/apps/kube-system/coredns/app/helmrelease.yaml55-57
DNSEndpoint CRD Pattern
While external-dns can automatically discover records from Ingress or HTTPRoute resources, the DNSEndpoint CRD is used for manual record definitions or for resources that do not natively support DNS annotations.
- Source Definition: The Cloudflare
external-dnsinstance is explicitly configured to useDNSEndpointas a source kubernetes/apps/network/external-dns/app/helmrelease.yaml34-35 - Implementation: The
cloudflare-tunnelapplication uses adnsendpoint.yamlfile to ensure the tunnel’s CNAME records are managed via GitOps kubernetes/apps/network/cloudflare-tunnel/app/kustomization.yaml5
Automation and Maintenance
- Reloader: All DNS-related components use the
reloader.stakater.com/autoannotation to automatically restart pods when their associatedSecretorConfigMap(e.g., API tokens or tunnel configs) changes kubernetes/apps/network/external-dns/app/helmrelease.yaml46kubernetes/apps/network/cloudflare-tunnel/app/helmrelease.yaml23 - Secret Management: API keys for Cloudflare, AdGuard, and OPNsense are managed via
ExternalSecretresources, which pull sensitive values from Bitwarden kubernetes/apps/network/external-dns/app/externalsecret.yaml14-30
Sources:
- kubernetes/apps/network/external-dns/app/helmrelease.yaml
- kubernetes/apps/network/external-dns/adguard-home/helmrelease.yaml
- kubernetes/apps/network/external-dns/unbound/helmrelease.yaml
- kubernetes/apps/network/cloudflare-tunnel/app/helmrelease.yaml
- kubernetes/apps/network/cloudflare-tunnel/app/resources/config.yaml
- kubernetes/apps/kube-system/coredns/app/helmrelease.yaml