Skip to main content

iptables Is Quietly Taxing Every New Connection on Your Fleet: The nftables Performance Case, With Numbers

9 min readDora NodaDora Noda
Share
On this page

Kubernetes 1.37 "Garhwal" shipped on August 26, 2026, and if your clusters never explicitly chose a kube-proxy mode, it started warning you about it. That warning is KEP-5343 in motion: the plan to make nftables the default kube-proxy backend, moving from alpha in 1.37 to beta in 1.39 to GA in 1.40. Your iptables-mode clusters keep working — iptables remains fully supported — but the default is moving under you, and every large-cluster performance investment upstream is landing in nftables.

Here is the honest correction to the hype first: nftables mode is not newly mature. It went GA back in Kubernetes 1.33. What changed in 2026 is the forcing function. Between the v1.37 warning on the implicit default and the IPVS backend's staged removal (deprecated in 1.35, disabled by default in 1.40, deleted in 1.43), the message from upstream is unambiguous: nftables is the one backend with a future. This post gives you the concrete before/after numbers for retiring iptables, what the switch actually costs, and a decision rule for when your fleet should flip.

The before/after numbers: what retiring iptables buys you

Every Kubernetes Service programs packet-filtering rules on every node, so the cost of the backend multiplies by your Service count on each machine. A multi-tenant fleet packing hundreds of tenant Services per node — each with its own ClusterIP chain plus a rule per endpoint — quietly accumulates tens of thousands of iptables rules per node, and every new connection walks them linearly. Published measurements make the gap between the two backends concrete:

Measurementiptables modenftables mode
First-packet latency, 5,000–10,000 Servicesp50 (average) baselinep50 roughly equals iptables' p01 (best case)
First-packet latency, 30,000 Servicesp01 best case beaten by nftables' p99p99 (worst case) beats iptables' best case
Rules for a 10,000-Service clusterHundreds of thousands of rulesA handful of sets and maps
kube-proxy idle memory157.5 MiB46.8 MiB (about 70 percent lower)
kube-proxy CPU during endpoint churn179 millicores133 millicores
Rule sync at 250,000 endpoints (kernel 6.12.55)Baseline77 percent faster synchronization

The latency rows come from Azure's AKS testing of kube-proxy backends at 5,000, 10,000, and 30,000 Services; the memory and CPU rows from an independent multi-CNI benchmark campaign that isolated the kube-proxy mode flip. Read the 30,000-Service row twice: nftables' worst-case connection-setup latency beats iptables' best case. That is not a tuning win. That is a different complexity class.

For a bex-sized fleet the absolute numbers are smaller — hundreds of tenant Services per node, not tens of thousands — but the shape is the same. iptables degrades linearly from the first Service; nftables stays flat. The tax you are paying today is measured in microseconds per connection and megabytes per node. It is small, it is real, and it grows with every tenant you add.

Why iptables degrades: linear walks plus full rewrites

Two mechanisms combine into the iptables tax, and both get worse as Service count grows.

First, packet matching is O(n). iptables mode creates one chain per Service plus one rule per endpoint, and a new connection evaluates those rules sequentially until it matches. With 500 tenant Services averaging a handful of endpoints each, a node holds tens of thousands of rules, and the average new connection scans a meaningful fraction of them before finding its DNAT target. nftables replaces this with verdict maps: set-based lookups that resolve in roughly constant time regardless of how many Services share the node.

Second, updates are full resyncs. Every endpoint change — a deploy, a scale event, a pod restart — makes iptables-mode kube-proxy rewrite a large portion of the ruleset, even when only one backend moved. On a busy multi-tenant cluster, endpoint churn is constant, so kube-proxy spends a steady slice of CPU re-emitting rules instead of converging. nftables mode applies incremental updates to its sets, which is why the CPU-during-churn row in the table favors it and why the gap widens on clusters where tenants deploy frequently.

One clarification that saves teams from misreading their dashboards: kube-proxy's CPU usage is per-reconcile, not per-packet. Once rules are programmed, packets traverse the kernel datapath without kube-proxy involved — you can restart kube-proxy with no traffic impact. The iptables tax shows up as sync latency after endpoint changes and connection-setup latency on new flows, not as steady-state forwarding overhead. That is also why the right metric to watch is kube_proxy_sync_proxy_rules_duration_seconds, not kube-proxy's average CPU.

What the switch actually costs: the itemized checklist

The performance case is one side of the ledger. Here is the other side, item by item, with nothing hand-waved.

Kernel floor: 5.13 or newer on every node. This is the likeliest source of scope creep. Any distribution shipped in the last few years qualifies — Ubuntu 22.04 ships 5.15, Debian 12 ships 6.1 — but a node image frozen years ago turns a one-line config change into a reimage project. Audit kernel versions across every pool before promising a quick flip.

CNI compatibility: check the matrix, then set the flags. kube-proxy's mode and your CNI's dataplane are separate choices that must coexist. Current guidance from the ecosystem: Cilium's eBPF dataplane needs no changes for a kube-proxy mode flip; Calico, Canal, and Flannel each have explicit nftables settings (Calico's linuxDataplane: Nftables or Felix iptablesBackend: nft, Flannel/Canal enableNFTables) that RKE2's July 2026 releases began wiring up alongside proxy-mode=nftables support. Read your CNI version's compatibility matrix rather than assuming — "it worked on iptables mode" is not evidence.

NetworkPolicy validation: enforced by the CNI, but verify anyway. NetworkPolicy is implemented by your CNI plugin, not by kube-proxy — kube-proxy only programs Service DNAT — so flipping the proxy mode does not change policy semantics. Still, the TODO-spec paranoia is correct: policies deserve a validation pass because the flip touches the same nodes' packet path. Run a canary pool through allow/deny policy tests (a denied pod-to-pod flow that stays denied, an allowed Service flow that stays allowed) before rolling the fleet. Policy behavior should be identical; "should be" is what staging is for.

The flip itself: one line plus a restart. Set mode: nftables in the kube-proxy ConfigMap and roll the DaemonSet. Upstream maintains a mode-switching end-to-end job confirming you can redeploy kube-proxy in a different mode on a live cluster, and rollback is confirmed reliable — restart with the old mode and the old ruleset returns.

The honest caveat: slower startup sync at scale. Upstream is tracking an issue where nftables-mode kube-proxy takes longer than iptables mode to complete its initial sync on very large clusters. For a fleet at hundreds of Services per node this is a footnote, not a blocker — but it is the reason to canary the flip on a production-shaped cluster and watch that first full resync, rather than assuming every dimension favors nftables.

The fallback that stays open. Unlike IPVS, iptables mode is not deprecated and has no removal date. If a pool cannot meet the kernel floor, it stays on a supported backend. The case for switching is performance and future-proofing, not a deadline — which conveniently means you can schedule it instead of scrambling for it.

The decision rule: when your fleet should flip

Not every cluster needs this next week. Apply this rule per cluster, using its own Service count:

  • Under ~1,000 Services: the latency delta is microseconds per connection. Switch anyway — the default is moving to nftables by 1.40 and new pools should be born on it — but schedule it as ordinary maintenance, not an incident. The v1.37 warning is your calendar entry.
  • 1,000–5,000 Services: the memory and sync-CPU wins are measurable, and endpoint churn starts making iptables-mode resyncs visible in kube_proxy_sync_proxy_rules_duration_seconds. Flip during your next maintenance window, canary first.
  • Over 5,000 Services: you are in the AKS benchmark regime where nftables' average beats iptables' best case. This is a p99-latency project with a one-line fix. Prioritize it.

Confirm with your own data before and after: scrape kube_proxy_sync_proxy_rules_duration_seconds across a deploy-heavy hour on iptables mode, flip the canary pool, and compare. Most teams see sync latency drop immediately; if yours doesn't, your cluster is small enough that the switch is pure future-proofing, and you have the graph to prove it.

Cluster API fleet specifics: audit everything, fix the templates

Managed-Kubernetes customers will experience the KEP-5343 default flip as a vendor-handled change. A self-hosted Cluster API fleet gets no concierge — every step is yours, and "every cluster" is the operative phrase:

  • Audit all clusters, not one. A fleet of ten clusters provisioned from slightly different templates can easily hide stragglers on the implicit default. Script the ConfigMap check across every management and workload cluster, staging included.
  • Fix the templates, not just the clusters. Flipping a live ConfigMap migrates today's cluster; setting mode: nftables in your cluster templates and node-image builds ensures the next provisioned pool is born on the future default. Otherwise you re-fix the same drift every time you scale.
  • Budget kernel checks into the plan. The 5.13 floor is the critical path on long-lived bare-metal pools. Verify node-image kernels before announcing the migration, especially anywhere the image predates 2022.
  • Schedule against the freeze calendar. Kubernetes 1.37's cycle — feature freeze early July, code freeze July 22, GA August 26 — gave operators a five-week window to audit against a frozen breaking-change list. Use the same rhythm for the 1.39/1.40 default-flip milestones: audit during freeze, migrate before GA.

If your fleet also has IPVS-mode clusters, handle those on the removal timeline — disabled by default in 1.40, deleted in 1.43 — and migrate them straight to nftables rather than hopping through iptables. One migration that lands on the final backend beats two.

Start during the warning stage

The staged rollout is a gift: v1.37 warns, 1.39 flips the default to beta, 1.40 makes it GA. Fleets that move during the warning stage pay an afternoon of ConfigMap edits, canary validation, and template updates. Fleets that wait pay the same work plus the surprise of a default changing under them on upgrade day — and keep paying the per-connection tax until then.

Run the mode audit across your fleet this week. If every cluster already sets its mode explicitly, you bought certainty for free. If the grep comes back with implicit defaults, you now have the numbers to justify the flip, the checklist to cost it, and the rule to schedule it.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex