Skip to content

Network

The cluster uses a single domain (PRIVATE_DOMAIN) split across two gateways — one for external access via Cloudflare, one for internal access via AdGuard Home DNS.

Physical Topology

flowchart TB
    ISP([ISP - fiber])
    ONT[ONT\n1 GbE WAN]
    Router[ASUS RT-AX58U\nAsuswrt-Merlin\n192.168.50.1]

    subgraph LAN[Home Network]
        Switch[NETGEAR GS108GE\n8-port unmanaged\n1 GbE]
        QNAP[QNAP TS-251D\n192.168.50.8]
        RPi[Raspberry Pi 4B\nHAOS + AdGuard Home addon\n192.168.50.9]
        Other[Other devices]
        WiFi[WiFi devices]

        subgraph K8S[Home Cluster - 192.168.48.x]
            mc1[mc1\n192.168.48.2]
            mc2[mc2\n192.168.48.3]
            mc3[mc3\n192.168.48.4]
        end
    end

    ISP --> ONT
    ONT -->|1 GbE| Router
    Router -->|1 GbE| Switch
    Router --> RPi
    Router --> Other
    Router -. WiFi .- WiFi
    Switch --> mc1
    Switch --> mc2
    Switch --> mc3
    Switch --> QNAP

Gateway Architecture

Gateway IP Access DNS Entry point
envoy-external 192.168.48.20 Internet Cloudflare Cloudflare Tunnel (cloudflared)
envoy-internal 192.168.48.21 Home network only AdGuard Home Direct L2 (Cilium)

Both gateways are implemented with Envoy Gateway using the Kubernetes Gateway API. TLS is terminated at the gateway using a wildcard certificate issued by cert-manager via Cloudflare DNS01 challenge.

DNS

Two external-dns controllers run in parallel, each scoped to its own gateway by annotation filter (external-dns.alpha.kubernetes.io/controller):

  • cloudflare-dns — watches resources annotated controller: external, writes records to Cloudflare DNS (proxied). Sources: DNSEndpoint CRDs + gateway-httproute from envoy-external.
  • adguard-dns — watches resources annotated controller: internal, writes records to AdGuard Home on the RPI (192.168.50.9). Sources: DNSEndpoint CRDs + gateway-httproute from envoy-internal.

Internal DNS records (AdGuard Home)

Static records are defined as DNSEndpoint CRDs in cluster/apps/system/adguard-dns/templates/dnsendpoints.yaml:

Record Type Target Purpose
k8s.PRIVATE_DOMAIN A 192.168.48.1 Cluster VIP (kube-apiserver)
qnap.PRIVATE_DOMAIN A 192.168.50.8 QNAP NAS

Per-app A records pointing to 192.168.48.21 are created automatically by adguard-dns external-dns from each HTTPRoute annotated with controller: dns-controller attached to envoy-internal.

External DNS records (Cloudflare)

Static records defined as DNSEndpoint CRDs:

Record Type Target Purpose
haas.PRIVATE_DOMAIN CNAME external.PRIVATE_DOMAIN Home Assistant (HAOS on RPi)

HTTPRoutes attached to envoy-external are automatically published to Cloudflare by external-dns.

External Access via Cloudflare Tunnel

cloudflared runs 2 replicas in the cluster and connects to Cloudflare's network. Incoming requests from the internet are routed by Cloudflare to the envoy-external gateway. No ports need to be forwarded on the home router.

IP Allocation

IP Service
192.168.48.1 Cluster VIP (kube-apiserver)
192.168.48.20 envoy-external gateway
192.168.48.21 envoy-internal gateway
192.168.48.23 Minecraft Bedrock
192.168.48.27 Home automation (Ollama, Whisper, Piper, OpenWakeWord)
192.168.48.28 Vintage Story
192.168.50.8 QNAP NAS
192.168.50.9 RPi — HAOS (Home Assistant OS); AdGuard Home as HA addon

[VIP]: Virtual IP (Used for high-availability controlplanes) [HA]: High Availability (often reduntant hardware/software) [PVC]: k8s resource - Persistent Volume Claim [PV]: k8s resource - Persistent Volume [CNI]: k8s networking - Container Network Interface [k8s]: Shortening of Kurbenetes [CRD]: Custom Resource Definitions [S3]: Simple Storage Service [NFS]: Network File System [DR]: Disaster Recovery