Skip to content

Cilium

Cilium is the CNI, replaces kube-proxy, announces LoadBalancer IPs over ARP, and runs the one Gateway every HTTPRoute attaches to. Cilium docs.

Namespacekube-system
Sourcecilium.go, stack platform
Gatewayhomelab-gateway (kube-system), Service cilium-gateway-homelab-gateway at 192.168.1.220
ExposureLAN · hubble.madhan.app
WhyCilium for networking
flowchart LR
  C[LAN client] -->|ARP 192.168.1.220| W[Worker holding<br/>the L2 lease]
  W --> E[Envoy<br/>homelab-gateway]
  E -->|HTTPRoute by hostname| S[Service → pod]

The Gateway has an HTTP listener on 80 and an HTTPS listener on 443 terminating with kube-system/wildcard-madhan-app-tls (cert-manager). Both accept routes from all namespaces; apps attach with kit.Route (CDK8s).

SettingValueWhy
kubeProxyReplacementtrueTalos runs no kube-proxy
k8sServiceHost / Port192.168.1.210 / 6443Without kube-proxy Cilium must reach the API server directly, via the VIP
devicesens18, eth0Nodes have carried both NIC names across Talos upgrades
bpf.hostLegacyRoutingtrueeBPF host routing conflicts with Talos’s host DNS forwarding and drops kubelet (10250) and apid (50000) traffic
socketLB.enabledfalseSocket LB hooks interfere with kubelet and apid binding their ports
installNoConntrackIptablesRulesfalseNOTRACK plus DROP-INVALID blocks new TCP to host ports
cgroup.autoMountfalse, hostRoot: /sys/fs/cgroupTalos mounts cgroups itself
l2announcements.enabledtrueARP for LoadBalancer IPs, no MetalLB
gatewayAPI.secretsNamespace.synctrueCilium owns the TLS copy in cilium-secrets; see cert-manager
IP pool address-pool192.168.1.220–230 from lb_pool, minus node IPsAnnouncing a node’s own IP takes that node off the LAN
L2 policy l2-policyWorkers only, loadBalancerIPs: true, externalIPs: false
deny-service-external-ipsValidatingAdmissionPolicykube-proxy replacement always serves spec.externalIPs (CVE-2020-8554); use a LoadBalancer Service
cilium-leases-accessClusterRole + bindingL2 leader election needs Lease access the chart does not grant

The pool range overlaps worker IPs .221–.224, so only .220 and .225–.230 are handed out. Fixed assignments use the lbipam.cilium.io/ips annotation (Ollama .225, CS2 .230).

The CRDs are vendored in core/platform/manifests/ and applied before Cilium: the operator enables Gateway support only for CRDs present at start-up. The experimental channel is used for TLSRoute v1alpha2. Fetching from the release URL was dropped because a rate-limited response fails the whole stack with an opaque unmarshal error. To upgrade, re-vendor the file and update its path in cilium.go. The bundle’s safe-upgrades admission policy rejects older CRDs; delete it first if you must roll back.

Terminal window
just core platform up
just gateway-repair-l2 # LAN times out on 192.168.1.220 but in-cluster requests work
kubectl -n kube-system exec ds/cilium -- cilium status | grep KubeProxyReplacement
kubectl get ciliumloadbalancerippool,ciliuml2announcementpolicy
kubectl get leases -n kube-system | grep l2announce

Upgrade one Cilium minor at a time; see Upgrades.

LoadBalancer IP stops answering after a Cilium restart

Section titled “LoadBalancer IP stops answering after a Cilium restart”

The lease holder keeps renewing but no longer answers ARP. Run just gateway-repair-l2, which deletes the lease so another node takes the IP. Details in Troubleshooting.

Deleting the pod does not clear corrupted BPF state. Reboot the node: talosctl --talosconfig ~/.talos/config reboot --nodes <ip>. If its datapath is broken the graceful reboot can hang; see Troubleshooting.