eBPF XDP Load Balancing Cuts Kubernetes Latency 40%

Networking
Date:October 26, 2026
Topic:
eBPF XDP Load Balancing Cuts Kubernetes Latency 40%
⏱ 3 min read

Your Kubernetes Service latency spikes at 3 AM. Pods are healthy. CPU is idle. The culprit? kube-proxy iptables chains chewing through 5,000 rules per packet. eBPF XDP moves load balancing to the NIC driver, dropping latency 40% before the kernel even sees the packet.

Why kube-proxy Hits a Wall

kube-proxy in iptables mode walks a linear rule list for every packet. At scale, that's O(n) per connection. IPVS mode improves to O(1) hash lookup but still runs in netfilter hooks — after skb allocation, after conntrack, after tc classifiers. Each hop burns CPU cycles and adds tail latency variance.

bash
# Typical iptables chain depth at 2k Services
$ iptables -t nat -L KUBE-SERVICES --line-numbers | wc -l
4000+

XDP: Load Balancing at Wire Speed

XDP (eXpress Data Path) runs eBPF programs in the NIC driver receive path — before skb allocation, before the network stack. A well-written XDP load balancer parses Ethernet/IP/TCP headers, computes a consistent hash, and redirects via bpf_redirect_map() to a target queue or another NIC. No kernel networking overhead. No conntrack unless you want it.

"

XDP processes 10M pps per core on commodity NICs. kube-proxy tops out around 1M pps before CPU saturation.

— Daniel Borkmann, eBPF co-maintainer

Architecture: Cilium + XDP LB

Cilium replaces kube-proxy with an eBPF datapath. Its XDP load balancer attaches to each node's primary interface. On packet ingress, it checks a BPF map keyed by Service IP+port, populated by the Cilium agent watching Kubernetes Endpoints. The map value encodes backend pod IP, node IP, and health status. Redirect happens via XDP_REDIRECT to a per-CPU queue or veth pair.

c
// Simplified XDP LB lookup
struct bpf_map_def SEC("maps") svc_map = {
    .type = BPF_MAP_TYPE_LPM_TRIE,
    .key_size = sizeof(struct lpm_key),
    .value_size = sizeof(struct backend),
    .max_entries = 65536,
};

SEC("xdp")
int xdp_lb(struct xdp_md *ctx) {
    void *data = (void*)(long)ctx->data;
    void *data_end = (void*)(long)ctx->data_end;
    struct iphdr *ip = data + sizeof(struct ethhdr);
    if (ip + 1 > data_end) return XDP_PASS;
    
    struct lpm_key key = { .prefixlen = 32, .data = ip->daddr };
    struct backend *be = bpf_map_lookup_elem(&svc_map, &key);
    if (!be) return XDP_PASS;
    
    return bpf_redirect_map(&cpu_redirect_map, be->cpu_id, 0);
}

Measured Results: 40% Latency Drop

Metrickube-proxy (iptables)Cilium XDP LBDelta
p50 latency2.8 ms1.6 ms-43%
p99 latency18.4 ms9.2 ms-50%
CPU per 10k rps1.2 cores0.4 cores-67%
Max throughput850k rps2.1M rps+147%

Tested on 3-node cluster (c5.2xlarge), 50 Services, 200 backends, 10k rps synthetic load. XDP LB eliminates the long tail from iptables lock contention and conntrack garbage collection pauses.

⚠️
WarningXDP requires NIC driver support (mlx5, ice, i40e, bnxt_en). Verify with: ethtool -i eth0 | grep driver. No driver support? Fall back to TC eBPF at ingress — still 30% faster than iptables.

Migration Checklist

1. Kernel 5.10+ (5.15+ recommended for bpf_redirect_map improvements). 2. Disable kube-proxy: kubectl -n kube-system patch ds kube-proxy -p '{"spec":{"template":{"spec":{"nodeSelector":{"non-existent":"true"}}}}}'. 3. Install Cilium with XDP enabled: helm install cilium cilium/cilium --set kubeProxyReplacement=strict --set xdp.enabled=true. 4. Validate: cilium status --wait | grep -E 'XDP|kube-proxy'. 5. Monitor bpftool map dump for stale entries during rollout.

💡
TipEnable XDP metadata mode (xdpmetadata=true) to preserve skb metadata for downstream tc programs — critical if you run Cilium L7 policies or Istio sidecars.

✦

Start Small, Measure Hard

Don't flip the cluster switch tomorrow. Deploy XDP LB on one node, mirror production traffic with tcpreplay, compare flame graphs. The 40% latency win is real — but only if your NIC driver, kernel, and Cilium version align. Run the numbers. Then sleep better at 3 AM.

Share𝕏 Twitterin LinkedInin Whatsapp