Files
homelab-design/decisions/0044-dns-and-external-access.md
Billy D. 5846d0dc16
All checks were successful
Update README with ADR Index / update-readme (push) Successful in 6s
docs: add ADRs 0043-0053 covering remaining architecture gaps
New ADRs:
- 0043: Cilium CNI and Network Fabric
- 0044: DNS and External Access Architecture
- 0045: TLS Certificate Strategy (cert-manager)
- 0046: Companions Frontend Architecture
- 0047: MLflow Experiment Tracking and Model Registry
- 0048: Entertainment and Media Stack
- 0049: Self-Hosted Productivity Suite
- 0050: Argo Rollouts Progressive Delivery
- 0051: KEDA Event-Driven Autoscaling
- 0052: Cluster Utilities (Spegel, Descheduler, Reloader, CSI-NFS)
- 0053: Vaultwarden Password Management

README updated with table entries and badge count (53 total).
2026-02-09 18:37:14 -05:00

7.2 KiB

DNS and External Access Architecture

  • Status: accepted
  • Date: 2026-02-09
  • Deciders: Billy
  • Technical Story: Design a multi-layer DNS and ingress architecture providing both public and private access to cluster services

Context and Problem Statement

A homelab behind a residential network needs to expose some services publicly (productivity apps, status pages) while keeping others private (admin UIs, AI inference). DNS resolution must work correctly for both external users and LAN clients, avoiding hairpin NAT issues.

How do we provide split-horizon DNS, secure external access, and automated DNS management across public and private domains?

Decision Drivers

  • No public IP dependency — residential NAT, dynamic IP
  • Split-horizon DNS — same domain resolves differently inside vs outside
  • Automated DNS record management from Kubernetes resources
  • External access without opening router ports
  • LAN clients should resolve directly to cluster IPs (no hairpin NAT)
  • Separate gateways for public vs internal services

Decision Outcome

A four-component DNS architecture with Cloudflare Tunnel for external access:

  1. Cloudflare Tunnel — encrypted tunnel for external traffic (no open ports)
  2. Cloudflare DNS (external-dns) — syncs public DNS records from HTTPRoutes
  3. k8s-gateway — internal DNS server for split-horizon resolution
  4. UniFi DNS (external-dns) — syncs internal DNS records to home network DNS

Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                     EXTERNAL ACCESS                                  │
│                                                                     │
│  Internet → Cloudflare CDN/Proxy                                    │
│                ↓                                                    │
│  Cloudflare Tunnel (QUIC + post-quantum encryption)                 │
│                ↓                                                    │
│  envoy-external (192.168.100.210)                                   │
│    *.daviestechlabs.io (Let's Encrypt wildcard)                     │
│    Services: Affine, Immich, Nextcloud, ntfy, Gatus, etc.           │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                     INTERNAL ACCESS                                  │
│                                                                     │
│  LAN client → UniFi DNS (home router)                               │
│                ↓ (*.lab.daviestechlabs.io → k8s-gateway VIP)        │
│  k8s-gateway (192.168.100.200, port 53)                             │
│                ↓ (resolves from HTTPRoutes/Services)                │
│  envoy-internal (192.168.100.201)                                   │
│    *.lab.daviestechlabs.io (self-signed + LE certs)                 │
│    Services: Grafana, Prometheus, MLflow, Companions, etc.          │
│    OIDC auth via Authentik                                          │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                     DNS AUTOMATION                                    │
│                                                                     │
│  HTTPRoute created → external-dns (Cloudflare) syncs public records │
│                    → external-dns (UniFi) syncs LAN records         │
│                                                                     │
│  Split-horizon: external client resolves to Cloudflare proxy IP     │
│                 LAN client resolves directly to cluster VIP         │
└─────────────────────────────────────────────────────────────────────┘

Component Details

Cloudflare Tunnel

Image cloudflare/cloudflared:2026.1.2
Transport QUIC with post-quantum encryption

Provides secure ingress without opening any router ports. All external traffic enters via Cloudflare's network and is tunneled to envoy-external over an encrypted QUIC connection.

  • Ingress: *.daviestechlabs.iohttps://envoy-external.network.svc.cluster.local:443
  • HTTP/2 origin, TLS verified against origin server name
  • DNSEndpoint CNAME: external.daviestechlabs.io<tunnel-id>.cfargotunnel.com
  • Resources: 10m CPU, 256Mi memory limit
  • Security: non-root (UID 65534), read-only rootfs, capabilities dropped

Cloudflare DNS (external-dns)

Chart external-dns v1.20.0
Provider Cloudflare

Watches gateway-httproute and DNSEndpoint CRDs, syncs to Cloudflare DNS. Records are Cloudflare-proxied (orange cloud) for DDoS protection. TXT prefix k8s. for ownership tracking. Sync policy: full lifecycle management.

k8s-gateway (Internal DNS)

Chart k8s-gateway v3.4.1
VIP 192.168.100.200 (port 53)

CoreDNS-based DNS server that resolves cluster service domains by watching HTTPRoute and Service resources. Provides split-horizon: LAN clients query this server (via UniFi DNS forwarding) and get direct cluster IPs instead of Cloudflare proxy IPs.

UniFi DNS (external-dns)

Chart external-dns v1.20.0
Webhook ghcr.io/kashalls/external-dns-unifi-webhook:v0.8.1

Syncs *.lab.daviestechlabs.io records to the UniFi controller's DNS server at 192.168.100.254. This means LAN devices automatically resolve internal services without manual DNS entries. API key from Vault via ExternalSecret.

Domain Strategy

Domain Gateway Access DNS Provider TLS
*.daviestechlabs.io envoy-external Public (Cloudflare Tunnel) Cloudflare Let's Encrypt wildcard
*.lab.daviestechlabs.io envoy-internal LAN only UniFi DNS Self-signed + LE