nftables vs iptables: A Kubernetes Platform Engineer's Perspective
A primer on Iptables and Nftables from K8s Perspective
Search for a command to run...
A primer on Iptables and Nftables from K8s Perspective
No comments yet. Be the first to comment.
A 502 Bad Gateway tells you that one proxy failed to get a valid response from another system. It does not tell you which layer failed, whether the application ever saw the request, or which timeout a

Cilium eBPF connection tracking, and how SNAT quietly turns stateless traffic into stateful

In Part 1 we got a verified ETCD snapshot back onto an Ansible control host, even with the cluster fully down. I've come across many a blog posts describing ETCD restoration process on a single node,
Part 1: Notes on taking frequent ETCD snapshots, and setting up an automated workflow to getting one back when the control plane is down and kubectl returns nothing but errors.
Documentation discipline done right

Notes From The Platform Layer
17 posts
Notes from the platform layer - distributed systems, production K8s, and the architecture decisions and trade-offs learnt the hard way. Increasingly at intersection of platform engineering and agentic AI: building real agents focused on reducing toil, increasing productivity and faster turnaround times.
K8s networking conversations these days seem to be dominated by discussions around Service meshes, and eBPF, yet many clusters today still rely on kube-proxy, and underneath kube-proxy lies a less glamorous but equally important question:
If you're a platform engineer running Kubernetes at any reasonable scale, this isn't merely an implementation detail. It directly affects how efficiently your cluster handles Svc routing, endpoint updates, rolling deploys, and autoscaling events.
What follows is my understanding of the concepts learnt the hard way while dealing with running on-prem k8s platforms at scale -
Imagine a Service with three backend Pods:
Svc A (10.96.0.10:80)
├── Pod 1 (10.244.1.10)
├── Pod 2 (10.244.2.10)
└── Pod 3 (10.244.3.10)
When a client sends traffic to the Service VIP:
client
|
v
10.96.0.10:80
the kernel must answer a simple question:
Which backend pod should receive this connection?
The answer depends on how kube-proxy programs the node.
Historically, kube-proxy implemented svc load balancing using iptables.
For every svc, kube-proxy creates a chain:
KUBE-SERVICES
|
└── KUBE-SVC-ABC123
Inside that chain are endpoint selection rules:
Rule 1 -> Pod 1
rule 2 -> Pod 2
Rule 3 -> Pod 3
In reality, these rules use probabilistic matching to distribute connections across backends.
Conceptually:
Connection arrives
|
v
Check Rule 1
|
+--> Match -> Pod 1
Otherwise
Check Rule 2
|
+--> Match -> Pod 2
Otherwise
Pod 3
The important detail is that iptables represents backend selection as a collection of rules.
That works well when you have a handful of svc's and endpoints.
Imagine:
1,000 Services
50 Endpoints per Service
Now kube-proxy may be managing tens of thousands of iptables rules.
For a packet destined to a Service, the kernel must first locate the matching Service rule and then evaluate randomised endpoint selection rules within that svc chain.
The lookup is more like:
Find Service
+
Find Endpoint
However, both of those operations become increasingly expensive as the number of Services and endpoints grows.
One of the most common misconceptions is that iptables struggles because packet forwarding becomes unbearably slow.
In practice, packet forwarding is rarely the primary issue as in a K8s clusters things are constantly changing:
pods are created and destroyed
Deployments roll forward
HPA scales workloads up and down
Nodes join and leave the cluster
Every endpoint change forces kube-proxy to reprogram iptables.
As endpoints are added and removed, kube-proxy repeatedly regenerates and reapplies large portions of its rule set.
This leads to:
Longer synchronization times
Higher kube-proxy CPU usage
Large iptables-restore ops
Slower propagation of endpoint updates
In large clusters, these effects become very noticeable.
nftables was designed as the successor to iptables.
The key architectural difference is subtle but important.
iptables treats networking state as rules.
nftables treats networking state as data.
Instead of representing backend selection as a series of rules, nftables can store information using efficient kernel data structures such as sets and maps.
Conceptually:
Service A
|
└── Backend Set
├── Pod 1
├── Pod 2
└── Pod 3
Rather than traversing chains of rules, the kernel can perform direct lookups into optimized structures.
Think of it like the difference between:
Searching a list
and
Looking up an entry in a hash table
The exact implementation apparently is more nuanced, but the above mental model more or less holds
The biggest benefit of nftables isn't just that packets suddenly move faster - It is operational efficiency.
Suppose a new Pod becomes available:
Pod 4 added
With iptables, kube-proxy may need to regenerate portions of the ruleset.
With nftables, the kernel can often update a set or map entry instead.
Conceptually:
Before:
[Pod1, Pod2, Pod3]
After:
[Pod1, Pod2, Pod3, Pod4]
The packet processing logic remains unchanged.
Only the data changes.
This makes endpoint updates significantly cheaper.
From my experience, the distinction becomes more important as cluster size grows. Consider two environments.
50 Nodes
200 Services
I personally have not found very any meaningful difference as imo iptables remains perfectly serviceable.
1,000+ Nodes
10,000+ Services
Tens of thousands of Endpoints
Now the operational overhead of maintaining large iptables rule sets becomes much more apparent. This is where nftables begins to show its strengths:
More efficient updatesnftables vs iptables: A Kubernetes Platform Engineer's Perspective
Kubernetes networking conversations these days seem to be dominated by discussions around Service Meshes, eBPF, and XDP. Yet many production clusters today still rely on kube-proxy, and underneath kube-proxy lies a less glamorous but equally important question:
Should your cluster be using iptables or nftables?
If you're a platform engineer running Kubernetes at any reasonable scale, this isn't merely an implementation detail. It directly affects how efficiently your cluster handles Service routing, endpoint updates, rolling deployments, and autoscaling events.
What follows is my understanding of these concepts, learned the hard way while operating large on-prem Kubernetes platforms.
Imagine a Service with three backend Pods:
Service A (10.96.0.10:80)
├── Pod 1 (10.244.1.10)
├── Pod 2 (10.244.2.10)
└── Pod 3 (10.244.3.10)
When a client sends traffic to the Service VIP:
Client
|
v
10.96.0.10:80
the kernel must answer a simple question:
Which backend Pod should receive this connection?
The answer depends on how kube-proxy programs the node.
Historically, kube-proxy implemented Service load balancing using iptables.
For every Service, kube-proxy creates a dedicated chain:
KUBE-SERVICES
|
└── KUBE-SVC-ABC123
Inside that chain are endpoint selection rules:
Rule 1 -> Pod 1
Rule 2 -> Pod 2
Rule 3 -> Pod 3
In reality, these rules use probabilistic matching to distribute new connections across backends.
Conceptually:
Connection arrives
|
v
Check Rule 1
|
+--> Match -> Pod 1
Otherwise
Check Rule 2
|
+--> Match -> Pod 2
Otherwise
Pod 3
The important detail is that iptables represents backend selection as a collection of rules.
That works perfectly fine when you have a handful of Services and endpoints.
Now imagine a larger environment:
1,000 Services
50 Endpoints per Service
At this point, kube-proxy may be managing tens of thousands of iptables rules.
For a packet destined to a Service, the kernel must first locate the matching Service rule and then evaluate endpoint selection rules within that Service chain.
The lookup is conceptually:
Find Service
+
Find Endpoint
Contrary to what some engineers assume, the kernel is not traversing every endpoint rule in the cluster. However, both Service lookups and endpoint selection become increasingly expensive as the number of Services and endpoints grows.
One of the most common misconceptions is that iptables struggles because packet forwarding becomes unbearably slow.
In practice, packet forwarding is rarely the primary issue.
The real challenge is change.
A Kubernetes cluster is a constantly moving target:
Pods are created and destroyed
Deployments roll forward
HPAs scale workloads up and down
Nodes join and leave the cluster
Every endpoint change forces kube-proxy to reprogram iptables.
As endpoints are added and removed, kube-proxy repeatedly regenerates and reapplies large portions of its rule set.
This leads to:
Longer synchronization times
Higher kube-proxy CPU utilization
Large iptables-restore operations
Slower propagation of endpoint updates
In sufficiently large clusters, these effects become very noticeable.
nftables was designed as the successor to iptables.
The key architectural difference is subtle but important.
iptables treats networking state as rules.
nftables treats networking state as data.
Instead of representing backend selection as a series of rules, nftables can store information using efficient kernel data structures such as sets and maps.
Conceptually:
Service A
|
└── Backend Set
├── Pod 1
├── Pod 2
└── Pod 3
Rather than traversing chains of rules, the kernel can perform direct lookups into optimized structures.
A useful mental model is:
iptables -> Search a list
nftables -> Lookup in a hash table
The actual implementation is more nuanced than that, but the comparison is directionally accurate.
The biggest benefit of nftables isn't that packets suddenly move faster.
The bigger win is operational efficiency.
Suppose a new Pod becomes available:
Pod 4 added
With iptables, kube-proxy may need to regenerate portions of the ruleset.
With nftables, the kernel can often update a set or map entry instead.
Conceptually:
Before:
[Pod1, Pod2, Pod3]
After:
[Pod1, Pod2, Pod3, Pod4]
The packet processing logic remains unchanged.
Only the underlying data changes.
This makes endpoint updates significantly cheaper and reduces the amount of work required during periods of high endpoint churn.
From my experience, the distinction becomes more important as cluster size grows.
50 Nodes
200 Services
I personally haven't observed a meaningful difference in day-to-day operations.
In environments of this size, iptables remains perfectly serviceable.
1,000+ Nodes
10,000+ Services
Tens of Thousands of Endpoints
Now the operational overhead of maintaining large iptables rule sets becomes much more apparent.
This is where nftables begins to show its strengths:
Faster synchronization
More efficient updates
Better scaling characteristics
Reduced rule management complexity
As platform engineers, it's mostly easy to get distracted by datapath benchmarks and packet-per-second numbers.
The more important question is:
how efficiently can my cluster adapt to change?
Modern Kubernetes environments are dynamic systems. Pods come and go constantly. Deployments roll all the time. Autoscalers react to traffic spikes, so basically - Endpoint churn is normal.
Viewed through that lens, nftables offers a cleaner and more scalable foundation than traditional iptables.
iptables treats Service routing as a collection of rules (if-elif-else construct) where as nftables treats svc routing as data (hash lookups)
That shift may sound subtle, but at k8s scale, it can make a significant difference.
And while kernel based xDP and eBPF-based solutions are increasingly becoming the industry's long-term direction, understanding the evolution from iptables to nftables remains essential for anyone building and operating modern Kubernetes platforms.