Kubernetes networking is notoriously complex. CNI plugins, kube-proxy, iptables chains, service meshes — layers upon layers of abstraction that eventually break in ways nobody understands.
Cilium changes this. It uses eBPF to move networking logic into the Linux kernel, bypassing iptables entirely. The result: better performance, more visibility, and network policies that actually make sense.
This is what I run in my clusters. Let me show you why.
What is eBPF?
eBPF (extended Berkeley Packet Filter) lets you run sandboxed programs in the Linux kernel without changing kernel source code or loading kernel modules.
flowchart TD
subgraph userspace["User Space"]
APP["Application"]
CILIUM["Cilium Agent"]
end
subgraph kernel["Kernel Space"]
EBPF["eBPF Programs"]
NET["Network Stack"]
HOOKS["Kernel Hooks<br/>(XDP, TC, Socket)"]
end
APP --> NET
CILIUM -->|"loads"| EBPF
EBPF --> HOOKS
HOOKS --> NET
Traditional networking uses iptables — a chain of rules evaluated sequentially. eBPF programs are compiled and executed directly in the kernel, with map-based lookups instead of linear rule scanning.
The difference at scale is dramatic. Iptables with 10,000 services? Performance nightmare. Cilium with 10,000 services? Constant-time lookups.
Installing Cilium
Replace your existing CNI with Cilium:
# Install Cilium CLI
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-amd64.tar.gz
sudo tar xzvfC cilium-linux-amd64.tar.gz /usr/local/bin
rm cilium-linux-amd64.tar.gz
# Install Cilium in cluster
cilium install --version 1.15.0
# Verify installation
cilium status --wait
For Helm-based GitOps deployment:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cilium
namespace: argocd
spec:
project: default
source:
repoURL: https://helm.cilium.io/
chart: cilium
targetRevision: 1.15.0
helm:
values: |
kubeProxyReplacement: true
k8sServiceHost: "10.0.0.1"
k8sServicePort: "6443"
hubble:
enabled: true
relay:
enabled: true
ui:
enabled: true
operator:
replicas: 2
ipam:
mode: kubernetes
destination:
server: https://kubernetes.default.svc
namespace: kube-system
kube-proxy Replacement
Cilium can completely replace kube-proxy, implementing Kubernetes Services in eBPF:
# Cilium values
kubeProxyReplacement: true
k8sServiceHost: "10.0.0.1" # API server IP
k8sServicePort: "6443"
Benefits:
- No iptables rules for services
- Direct server return for better performance
- Socket-level load balancing — decisions made at connect(), not per-packet
- Maglev hashing for consistent backend selection
Verify it’s working:
kubectl -n kube-system exec ds/cilium -- cilium status | grep KubeProxyReplacement
# KubeProxyReplacement: True [eth0 10.0.0.10 (Direct Routing)]
Network Policies
Cilium implements standard Kubernetes NetworkPolicy plus extended CiliumNetworkPolicy:
Kubernetes NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-ingress
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
CiliumNetworkPolicy (Extended)
Cilium’s native policies offer more power:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-policy
namespace: production
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: "GET"
path: "/api/v1/.*"
- method: "POST"
path: "/api/v1/orders"
Notice the rules.http — Cilium can enforce L7 policies, allowing specific HTTP methods and paths. No service mesh required.
DNS-Based Policies
Allow traffic to external services by DNS name:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-external-api
spec:
endpointSelector:
matchLabels:
app: backend
egress:
- toFQDNs:
- matchName: "api.stripe.com"
- matchPattern: "*.amazonaws.com"
toPorts:
- ports:
- port: "443"
Cilium intercepts DNS queries and dynamically updates policies when IPs change.
Cluster-Wide Policies
Apply policies across all namespaces:
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: deny-external-by-default
spec:
endpointSelector: {}
egress:
- toEntities:
- cluster
- host
- toFQDNs:
- matchPattern: "*.internal.company.com"
egressDeny:
- toEntities:
- world
This denies internet access by default, allowing only internal traffic unless explicitly permitted.
Hubble: Network Observability
Hubble is Cilium’s observability layer. It shows you what’s actually happening on your network.
# Enable Hubble
hubble:
enabled: true
relay:
enabled: true
ui:
enabled: true
metrics:
enabled:
- dns
- drop
- tcp
- flow
- icmp
- http
Hubble CLI
# Install Hubble CLI
export HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
curl -L --fail --remote-name-all https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-amd64.tar.gz
sudo tar xzvfC hubble-linux-amd64.tar.gz /usr/local/bin
# Port-forward to Hubble Relay
cilium hubble port-forward &
# Observe flows
hubble observe --namespace production
# Filter by pod
hubble observe --pod production/api-xyz123
# Filter by verdict
hubble observe --verdict DROPPED
# Filter by HTTP
hubble observe --protocol http --http-status 500
Hubble UI
Access the visual network flow map:
cilium hubble ui
# Opens browser to http://localhost:12000
The UI shows:
- Service dependency graph
- Real-time traffic flows
- Policy verdicts (allowed/dropped)
- HTTP request/response details
Hubble Metrics
Export to Prometheus:
hubble:
metrics:
serviceMonitor:
enabled: true
enabled:
- dns:query;ignoreAAAA
- drop
- tcp
- flow
- icmp
- http
Useful metrics:
hubble_flows_processed_total— Total observed flowshubble_drop_total— Dropped packets by reasonhubble_http_requests_total— HTTP requests by method, statushubble_dns_queries_total— DNS queries by type, response
Service Mesh (Without Sidecars)
Cilium can provide service mesh features without sidecar proxies:
# Enable L7 proxy
l7Proxy: true
# Enable mutual TLS
authentication:
mutual:
spiffe:
enabled: true
trustDomain: cluster.local
Features available:
- mTLS between services (using SPIFFE identities)
- L7 load balancing with retries
- Traffic management (canary, header-based routing)
- Observability (L7 metrics, distributed tracing)
The key difference: no sidecar containers. The eBPF dataplane handles everything, reducing resource overhead and latency.
apiVersion: cilium.io/v2
kind: CiliumEnvoyConfig
metadata:
name: api-routing
spec:
services:
- name: api
namespace: production
backendServices:
- name: api-v1
namespace: production
- name: api-v2
namespace: production
resources:
- "@type": type.googleapis.com/envoy.config.listener.v3.Listener
# ... Envoy configuration for traffic splitting
BGP for Bare Metal
Running on bare metal without a cloud load balancer? Cilium speaks BGP:
# Enable BGP
bgpControlPlane:
enabled: true
apiVersion: cilium.io/v2alpha1
kind: CiliumBGPPeeringPolicy
metadata:
name: rack-bgp
spec:
nodeSelector:
matchLabels:
rack: rack-1
virtualRouters:
- localASN: 65001
exportPodCIDR: true
neighbors:
- peerAddress: "10.0.0.1/32"
peerASN: 65000
Your pods get routable IPs, advertised via BGP to your network infrastructure.
Bandwidth Management
Cilium can enforce bandwidth limits:
apiVersion: v1
kind: Pod
metadata:
annotations:
kubernetes.io/ingress-bandwidth: "10M"
kubernetes.io/egress-bandwidth: "10M"
spec:
containers:
- name: app
image: my-app:latest
Implemented in eBPF, not tc rules. More efficient, more predictable.
Encryption
Encrypt all pod-to-pod traffic:
# WireGuard encryption (recommended)
encryption:
enabled: true
type: wireguard
# Or IPsec
encryption:
enabled: true
type: ipsec
WireGuard is faster and simpler. IPsec if you need compliance certifications that specifically require it.
Verify encryption:
kubectl -n kube-system exec ds/cilium -- cilium status | grep Encryption
# Encryption: Wireguard [NodeEncryption: Disabled, cilium_wg0 (Pubkey: xxx, Port: 51871, Peers: 2)]
Multi-Cluster with Cluster Mesh
Connect multiple Cilium clusters:
# Enable Cluster Mesh on each cluster
cilium clustermesh enable
# Connect clusters
cilium clustermesh connect --destination-context cluster2
Features:
- Global services — Same service accessible from any cluster
- Cross-cluster network policies — Allow traffic from cluster1 to cluster2 pods
- Shared identities — Consistent security policies across clusters
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-from-cluster1
spec:
endpointSelector:
matchLabels:
app: api
ingress:
- fromEndpoints:
- matchLabels:
io.cilium.k8s.policy.cluster: cluster1
app: frontend
Troubleshooting
Check Endpoint Status
kubectl -n kube-system exec ds/cilium -- cilium endpoint list
Check Policy Verdicts
kubectl -n kube-system exec ds/cilium -- cilium policy get
Debug Dropped Traffic
hubble observe --verdict DROPPED
Check BPF Maps
kubectl -n kube-system exec ds/cilium -- cilium bpf lb list
kubectl -n kube-system exec ds/cilium -- cilium bpf endpoint list
My Production Configuration
cilium:
kubeProxyReplacement: true
k8sServiceHost: "10.0.0.1"
k8sServicePort: "6443"
ipam:
mode: kubernetes
# Enable Hubble observability
hubble:
enabled: true
relay:
enabled: true
ui:
enabled: true
metrics:
enabled:
- dns:query
- drop
- tcp
- flow
- http
# WireGuard encryption
encryption:
enabled: true
type: wireguard
# L7 policies
l7Proxy: true
# Operator HA
operator:
replicas: 2
# Resource limits
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 100m
memory: 256Mi
Key decisions:
- kube-proxy replacement — Simpler, faster
- WireGuard encryption — Zero-config encryption
- Hubble metrics — Export to Prometheus for alerting
- L7 proxy — HTTP-aware policies without service mesh
Why Cilium?
Traditional CNI plugins work. Calico, Flannel, Weave — they all route packets. But they’re building on iptables, a technology from 1998.
Cilium represents the future:
- eBPF — Programmable dataplane in the kernel
- Identity-based — Policies based on what, not where
- Observable — See actual traffic, not just rules
- Extensible — Service mesh, BGP, encryption in one tool
For understanding your network, Cilium gives you visibility that iptables-based solutions can’t match. For security, L7-aware policies catch what L3/L4 can’t. For performance, eBPF beats linear rule evaluation.
This is what modern Kubernetes networking looks like.
iptables served us well for decades. But Kubernetes demands something more dynamic, more observable, more powerful. eBPF is that something, and Cilium is how you use it.
