Networking¶
This document covers the networking stack from external client access down to individual pods, including load balancing, gateway routing, TLS termination, DNS, and the VPN sidecar architecture.
Network Architecture¶
flowchart TD
client["Client Browser"] -->|"HTTPS request\n*.homelab.local"| dns["DNS Resolution"]
dns --> ciliumVIP["Cilium L2 VIP\n192.168.10.200-250"]
ciliumVIP --> gateway["Cilium Gateway\n(homelab-gateway)"]
gateway -->|"TLS termination"| certManager["cert-manager\n(homelab-ca-issuer)"]
gateway -->|"Route by hostname\n(HTTPRoute)"| appSvc["Application Service"]
appSvc --> appPod["Application Pod"]
Cilium Gateway API¶
Cilium serves as both the CNI and the gateway controller. The homelab-gateway Gateway resource accepts HTTPS traffic on port 443 for *.homelab.local and terminates TLS using a cert-manager-issued wildcard certificate.
Each application defines an HTTPRoute that binds to the gateway and routes by hostname:
route:
main:
enabled: true
kind: HTTPRoute
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: homelab-gateway
namespace: default
sectionName: https
hostnames:
- <app>.homelab.local
An HTTP-to-HTTPS redirect is configured via a separate HTTPRoute on the gateway's HTTP listener.
Key Resources¶
| Resource | Kind | Purpose |
|---|---|---|
homelab-gateway |
Gateway | Central entry point for all HTTPS traffic |
homelab-pool |
CiliumLoadBalancerIPPool | Allocates IPs from 192.168.10.200-250 |
homelab-l2 |
CiliumL2AnnouncementPolicy | Advertises allocated IPs via ARP on the LAN |
| Per-app routes | HTTPRoute | Hostname-based routing to each application service |
Cilium L2 Announcements¶
Cilium replaces MetalLB for LoadBalancer IP allocation. It operates in Layer 2 mode, responding to ARP requests on the local network to advertise virtual IPs from the homelab-pool.
- IP Address Pool:
192.168.10.200-192.168.10.250 - Mode: L2 ARP announcement
- Consumers: The
homelab-gatewayGateway and any LoadBalancer services (e.g., Jellyfin)
L2 Limitations
In L2 mode, all traffic for a given VIP is handled by a single node (the current ARP responder). Failover occurs when the node becomes unavailable, at which point another node takes over the VIP.
Cert-Manager and the CA Chain¶
TLS certificates are issued by a self-signed CA chain managed entirely within the cluster by cert-manager.
flowchart TD
selfSignedIssuer["ClusterIssuer:\nselfsigned-issuer"] -->|"issues"| caCert["Certificate:\nhomelab-ca"]
caCert -->|"stored in"| caSecret["Secret:\nhomelab-ca-secret"]
caSecret -->|"referenced by"| caIssuer["ClusterIssuer:\nhomelab-ca-issuer"]
caIssuer -->|"issues certs for"| gatewayTLS["Gateway TLS Secret"]
CA Chain Components¶
| Resource | Kind | Purpose |
|---|---|---|
selfsigned-issuer |
ClusterIssuer | Bootstrap issuer that signs the CA certificate |
homelab-ca |
Certificate | The root CA certificate, signed by the self-signed issuer |
homelab-ca-secret |
Secret | Stores the CA key pair, referenced by the CA issuer |
homelab-ca-issuer |
ClusterIssuer | Issues TLS certificates for the Gateway using the CA |
The Gateway resource includes a cert-manager.io/cluster-issuer: homelab-ca-issuer annotation. cert-manager automatically provisions a wildcard TLS certificate for *.homelab.local and stores it in the homelab-gateway-tls Secret.
Trusting the CA
To avoid browser warnings, import the homelab-ca certificate into your operating system or browser trust store. The CA certificate can be extracted from the homelab-ca-secret Secret in the cert-manager namespace.
DNS¶
All services use the *.homelab.local domain pattern. DNS resolution is handled at the network level (router or local DNS server) pointing *.homelab.local to the Cilium L2 VIP assigned to the homelab-gateway Gateway.
Application Hostnames¶
| Application | Hostname |
|---|---|
| Jellyfin | jellyfin.homelab.local |
| Sonarr | sonarr.homelab.local |
| Radarr | radarr.homelab.local |
| Prowlarr | prowlarr.homelab.local |
| Bazarr | bazarr.homelab.local |
| Seerr | seerr.homelab.local |
| qBittorrent | qbit.homelab.local |
| Tdarr | tdarr.homelab.local |
| Homepage | home.homelab.local |
| Uptime Kuma | status.homelab.local |
| Authentik | auth.homelab.local |
| Grafana | grafana.homelab.local |
| Prometheus | prometheus.homelab.local |
| Alertmanager | alertmanager.homelab.local |
| Vault | vault.homelab.local |
| OpenClaw | openclaw.homelab.local |
Network Policies¶
CiliumNetworkPolicies enforce namespace-level ingress isolation. Each application namespace has a default-deny rule for external traffic, with explicit allow rules for the gateway, intra-namespace communication, and Prometheus scraping. See the Network Policies infrastructure page for the full policy breakdown per namespace.
VPN Sidecar Architecture¶
qBittorrent runs alongside a Gluetun VPN container in a shared pod. Both containers share a single network namespace, meaning all egress traffic from qBittorrent routes through the VPN tunnel.
flowchart TD
subgraph vpnPod["Pod: gluetun-qbit"]
gluetun["Gluetun Container\n(PIA VPN)"]
qbit["qBittorrent Container"]
gluetun ---|"shared network\nnamespace"| qbit
end
subgraph network["Network Flow"]
internet["Internet"]
vpnTunnel["VPN Tunnel\n(PIA)"]
localNet["Local Network\n192.168.10.0/24"]
end
gluetun -->|"all egress via\nVPN tunnel"| vpnTunnel
vpnTunnel --> internet
localNet -->|"port 8080 (qBit)"| gluetun
internet -->|"dynamic port\n(PIA port forwarding)"| gluetun
Gluetun Configuration¶
| Setting | Value |
|---|---|
| VPN Provider | Private Internet Access (PIA) |
| Server Region | CA Montreal |
| Capability | NET_ADMIN (required for VPN tunnel creation) |
| Firewall - qBittorrent | Port 8080 |
| Port Forwarding | VPN_PORT_FORWARDING=on (PIA assigns port dynamically) |
| Credentials | ExternalSecret (vpn-credentials, synced from Vault) |
Gluetun creates the VPN tunnel interface and configures iptables firewall rules. The NET_ADMIN capability is required for tunnel and firewall management. Because the containers share a network namespace, qBittorrent automatically uses the VPN tunnel for all outbound connections.
Kill Switch
If the VPN tunnel drops, Gluetun's built-in firewall rules prevent any traffic from leaving the pod outside the tunnel. This acts as a kill switch ensuring download traffic is never exposed on the local network.