kube-proxy Modes in Kubernetes: iptables vs IPVS vs eBPF

kube-proxy Modes in Kubernetes: iptables vs IPVS vs eBPF

How a Service's stable address turns into real traffic reaching a real Pod, and how three different engines inside the Linux kernel do that job with very different costs, speeds and trade-offs.

Think of a large company with a single customer helpline number. You dial it, and a call centre quietly connects you to whichever agent is free. Agents join and leave all day, but the number never changes. Behind the scenes there is a switchboard that keeps an up-to-date list of agents and decides where each call goes. The caller never sees the switchboard. In Kubernetes the helpline number is a Service address, the agents are Pods, and the switchboard is the machinery that kube-proxy sets up on every node.

There is more than one way to build that switchboard. One uses a long list of packet-filtering rules, another uses a purpose-built load-balancing table, a third uses small programs that run directly inside the kernel. Each has strengths and weaknesses that only show up at scale, under failure or during debugging. This post explains all of them from the beginning. You do not need networking expertise. Every term is defined, compared with something from daily life, and then used in the next section.

1The Problem: Pods Come and Go, Clients Need a Stable Address

Start with why Kubernetes needs a switchboard at all.

Pods are temporary

A Pod is the smallest unit that runs your containers, and every Pod gets its own IP address. But Pods are disposable. They are replaced during updates, rescheduled when a node fails, and created or removed as load changes. Each replacement gets a brand new IP address. If a front-end program stored the address of a back-end Pod, it would break the next time that Pod was replaced, which in a busy cluster could be minutes later.

Simple Analogy

Imagine writing a friend’s temporary hotel room number in your address book. Next week they move to a different hotel, and the number is useless. What you really need is a permanent contact, such as a phone number that follows them wherever they go. A Service is that permanent contact for a group of Pods.

Many Pods behind one name

Usually a Service fronts several identical Pods, so the same request can be answered by any of them. That means somebody must choose which Pod receives each new connection. This is load balancing. The choice must be fast, because it happens for every connection, and it must follow changes quickly, because Pods appear and disappear all the time.

Where the choice can be made

Option 1

In the client program

Every application would need to know how to look up and balance across Pods. That means every language and every team repeats the same logic.

Option 2

In a central load balancer

All traffic crosses one extra hop and one extra bottleneck, which is slower and a single point of failure.

Option 3

On every node, in the kernel

Each node rewrites destinations locally, so there is no extra hop and no central bottleneck. This is the Kubernetes approach, and kube-proxy is the program that sets it up.

!
Common Misconception

Many beginners picture kube-proxy as a program that every packet flows through, like a traditional proxy server. In modern Kubernetes it usually does not touch your traffic at all. It only writes instructions into the kernel, and the kernel moves the packets. The rest of this post builds on that distinction.

2Services and Endpoints: A Quick Refresher

The vocabulary you need before looking inside the switchboard.

What a Service is

A Service is a Kubernetes object that gives a group of Pods a stable name and a stable virtual IP address. It picks its Pods using labels. The virtual address is called the ClusterIP. It is virtual because it is not attached to any real network card. No machine answers at that address. It exists only as a rule in the kernel’s packet handling.

Endpoint slices

The list of real Pod addresses behind a Service is stored in objects called EndpointSlices. A controller keeps them up to date as Pods become ready, fail or disappear. Large Services are split across several slices, which keeps each object small and makes updates cheap. kube-proxy reads these slices to know where traffic may be sent.

Type

ClusterIP

The default. Reachable only from inside the cluster, through the virtual address.

Type

NodePort

Opens the same port on every node, so outside clients can reach the Service by connecting to any node.

Type

LoadBalancer

Asks the cloud provider for an external load balancer that forwards into the NodePorts.

Type

Headless

No virtual address at all. DNS returns Pod addresses directly, so kube-proxy does nothing for it.

Simple Analogy

A Service is the company’s published helpline number. EndpointSlices are the staff roster showing which agents are currently on shift. kube-proxy is the technician who keeps each office’s phone system updated whenever the roster changes.

What DNS adds

People rarely type the virtual address. Cluster DNS gives each Service a name, so a program can connect to a friendly name that resolves to the ClusterIP. DNS itself is handled by a separate component and has nothing to do with kube-proxy modes. DNS answers “what is the address of this Service,” and kube-proxy’s work starts when a packet is sent to that address.

i
Two Separate Questions

Keep two questions apart: “which address does this name mean?” is answered by DNS, and “which Pod receives this connection?” is answered by the kube-proxy machinery. Confusing them is a common source of debugging detours.

3What kube-proxy Does, and What It Does Not

A small program with one job: translate cluster objects into kernel instructions.

The job in one sentence

kube-proxy runs on every node, watches Services and EndpointSlices through the API server, and programs the node’s kernel so that traffic sent to a Service address gets redirected to one of the healthy Pods behind it. It is a translator between the Kubernetes world of objects and the Linux world of network rules.

1

Watch

It keeps an up-to-date local picture of Services and EndpointSlices using the same watch and informer machinery that other controllers use.

2

Compute

It works out what the node’s forwarding rules should look like for the current set of Services and ready Pods.

3

Program

It writes those rules into the kernel, in a way that depends on the chosen mode.

4

Repeat

Whenever something changes, and also periodically as a safety net, it repeats the cycle so the kernel never drifts far from reality.

What it does not do

It does not carry your packets

In the modes used today, packets are rewritten and forwarded entirely by the kernel. If kube-proxy crashes, existing rules stay in place and traffic keeps flowing with the last known state, though new changes will not be applied until it returns.

It does not enforce network policies

Rules about which Pods may talk to which Pods are the job of the cluster network plugin, not kube-proxy. People often blame kube-proxy for blocked traffic that a policy actually caused.

It does not provide DNS or service discovery names

Names come from cluster DNS. kube-proxy only handles addresses.

It does not do application-level routing

Decisions based on web paths, headers or cookies belong to ingress controllers and service meshes. kube-proxy works at the connection level and knows nothing about the contents of your requests.

A short history

The earliest design really did proxy traffic through a user-space program, which was slow because every packet had to cross between kernel and program. It was replaced by the rule-based approach, first with iptables, then with IPVS as an alternative, and more recently with nftables as a modern successor. The old user-space mode has been removed from current releases. Separately, some network plugins now take over the whole job using eBPF and no longer run kube-proxy at all. These are the engines we compare in this post.

i
Choosing the Mode

The mode is a setting in kube-proxy’s configuration. Available modes and defaults depend on the operating system and the Kubernetes version, so always read the release notes of your version before choosing. On Linux the default has long been iptables.

4The Control Loop: From Object to Kernel Rule

Whatever the mode, the flow from a Kubernetes change to a kernel change looks the same.

Following one change

Suppose a new Pod for a Service becomes ready. The endpoint controller adds its address to an EndpointSlice. kube-proxy on every node has a watch open on EndpointSlices, so each instance receives the change within moments. It updates its internal picture, notes that the Service’s rules are now out of date, and schedules a rule update. Updates are deliberately batched, so a burst of changes leads to one rule sync rather than hundreds.

sequenceDiagram
  participant P as New Pod
  participant E as Endpoint controller
  participant A as API Server
  participant K as kube-proxy on each node
  participant N as Node kernel
  P->>A: Pod becomes ready
  E->>A: Add Pod address to EndpointSlice
  A-->>K: Watch event: EndpointSlice changed
  K->>K: Update local picture and mark Service dirty
  K->>K: Wait for batching interval
  K->>N: Program updated rules
  Note over N: New connections can now reach the new Pod
        
FIG 1 — A Pod becomes ready and every node’s kernel learns about it through kube-proxy

Batching and timing

kube-proxy has two timing ideas. One is a minimum gap between syncs, so that a storm of changes does not cause continuous reprogramming. The other is a periodic full sync, which re-applies the entire desired state even if no event arrived, repairing any damage done by something else. These intervals trade freshness against cost. Short gaps make new Pods reachable sooner, long gaps save CPU on busy clusters.

Simple Analogy

A receptionist updates the staff board once every few minutes with all the changes, instead of walking to the board after every single person who arrives. Once a day the receptionist also redoes the whole board from the master roster, just in case someone scribbled on it.

Readiness matters

Only Pods that report themselves as ready receive traffic. A Pod that is still starting, or that has failed its readiness check, is left out of the rules. This is why readiness checks are so important to a smooth rollout: they decide when the switchboard starts sending calls to a new agent. Pods that are shutting down are treated specially in newer versions, so existing connections can finish while no new connections arrive.

i
How Fast Is Fast?

In a healthy cluster, the delay between a Pod becoming ready and every node being able to reach it is usually a second or two, though it can be longer on very large clusters with heavy rule sets. This delay, often called network programming latency, is one of the most useful numbers to watch.

5iptables Mode: Load Balancing with Packet Rules

The long-standing default. It works almost everywhere and is simple to reason about, up to a point.

What iptables is

iptables is a Linux tool for managing the kernel’s packet-filtering rules. Rules are organised into tables and chains, and each packet walks through the chains and is matched against rules in order. A rule can accept a packet, drop it, or change its destination. Changing the destination is called destination NAT, and it is the trick that makes Services work: a packet addressed to the virtual IP has its destination rewritten to a real Pod address.

Simple Analogy

Imagine a mail sorting room where each letter passes a long row of clerks. The first clerk checks if it is for the helpline, the next clerk checks another address, and so on. When a clerk finds a match, they cross out the address and write a real person’s address instead. The longer the row of clerks, the longer each letter takes to sort.

How the rules are organised

kube-proxy creates a hierarchy of custom chains. A top-level chain holds one entry per Service address and port. Each of those jumps to a per-Service chain, and the per-Service chain holds one entry per Pod behind it. The per-Pod entry performs the actual address rewriting. To spread load, the per-Service chain uses a random-probability rule: the first Pod gets a chosen probability, the next one a larger share of what is left, and the last one takes the remainder, which produces an even random split.

flowchart TD
  P["Packet to Service IP and port"] --> T["Top-level services chain"]
  T --> M{"Matches a Service entry?"}
  M -- "No" --> X["Continue normal handling"]
  M -- "Yes" --> S["Per-Service chain"]
  S --> R{"Random pick by probability"}
  R --> E1["Per-Pod rule 1: rewrite to Pod 1"]
  R --> E2["Per-Pod rule 2: rewrite to Pod 2"]
  R --> E3["Per-Pod rule 3: rewrite to Pod 3"]
        
FIG 2 — iptables mode: a chain of chains, from service match to random Pod pick to destination rewrite

The return path and connection memory

Once a connection’s first packet has been rewritten, the kernel’s connection tracking table remembers the choice. Every later packet of that connection follows the same mapping without walking the chains again, and reply packets are automatically rewritten back so the client believes it is talking to the Service address. This memory is why changing the rules does not break connections that are already open.

Advantages

  • Available on practically every Linux system with no extra kernel modules to load
  • Long track record and very widely understood by operators
  • Easy to inspect with familiar tools
  • No special dependencies, which makes it a safe default
  • Good enough for the majority of small and medium clusters

Disadvantages / Trade-offs

  • Rule matching grows with the number of Services and Pods, so very large clusters slow down
  • Updating rules means rewriting large rule sets, which costs CPU and can briefly lock the table
  • Load balancing is random only, with no smarter strategies
  • Debugging thousands of auto-generated rules is tiring
  • Rule churn in busy clusters can delay how quickly new Pods become reachable
!
Where It Starts to Hurt

The trouble usually shows up in clusters with thousands of Services and many tens of thousands of Pods. Matching a packet against a long chain, and rewriting a very large rule set on every change, become noticeable costs. Exact thresholds depend on hardware, kernel and kube-proxy version, so measure your own cluster rather than trusting rules of thumb.

6IPVS Mode: A Purpose-Built Load Balancer in the Kernel

Instead of a long list of rules, use the kernel’s own virtual server feature, built for exactly this job.

What IPVS is

IPVS, short for IP Virtual Server, is a load-balancing feature that lives inside the Linux kernel. It was designed for large-scale load balancing long before Kubernetes existed. It keeps a table of virtual services, each with a list of real servers behind it, and looks them up using hash tables, so finding the right virtual service takes about the same time whether there are ten Services or ten thousand.

Simple Analogy

Instead of a long row of clerks checking every letter one by one, imagine a single sorting machine with an index card for every address. The machine looks up the card instantly, finds the list of real recipients, and picks one using a chosen strategy. The size of the index barely changes how long a lookup takes.

How kube-proxy uses it

In IPVS mode, kube-proxy creates one virtual server for each Service address and port, and adds one real server for each ready Pod. The Service addresses are attached to a dummy network interface on the node, so the kernel treats them as local addresses and hands matching packets to IPVS. A small number of packet-filter rules and address sets still exist alongside IPVS to handle tasks such as marking packets that need address masquerading. So IPVS mode is not entirely rule-free, but the number of rules stays small and mostly constant regardless of how many Services exist.

flowchart TD
  P["Packet to Service IP"] --> D["Service IP is local on a dummy interface"]
  D --> V["IPVS virtual server table lookup by hash"]
  V --> SCH{"Scheduling algorithm"}
  SCH --> R1["Real server: Pod 1"]
  SCH --> R2["Real server: Pod 2"]
  SCH --> R3["Real server: Pod 3"]
        
FIG 3 — IPVS mode: a hash lookup finds the virtual server, then a scheduling algorithm picks the Pod

Choice of scheduling algorithm

One real advantage of IPVS is that it offers several ways to choose the real server, not just a random pick. The choice is configurable in kube-proxy.

Algorithm

Round robin

Takes turns in order. Simple, predictable and the usual default.

Algorithm

Least connection

Sends new connections to the Pod currently holding the fewest active ones, helpful when connection lengths vary a lot.

Algorithm

Source hashing

Maps a given client address to the same Pod each time, giving a basic form of stickiness without cookies.

Algorithm

Destination hashing and others

Other variants exist, such as shortest expected delay and never queue, for special traffic patterns.

Requirements and caveats

IPVS needs specific kernel modules loaded on every node. Missing modules are one of the commonest reasons for failures when switching modes, and some minimal or locked-down operating systems do not include them. IPVS also keeps its own connection table with its own timeouts, separate from the general connection tracking, which means that some long-idle connections may be dropped by timeout settings that differ from what teams are used to. Tuning these timeouts is a known part of running IPVS well.

Advantages

  • Constant-time lookups, so large numbers of Services barely slow packet handling
  • Incremental updates change only what is needed, not an entire rule set
  • Several load-balancing algorithms to choose from
  • Mature kernel feature with a long history in load-balancer products

Disadvantages / Trade-offs

  • Needs extra kernel modules and still uses some packet-filter rules and address sets
  • Its own connection timeouts can surprise teams with long-lived idle connections
  • Fewer people know how to debug it compared with iptables
  • The project has signalled a move away from this mode in recent releases, so check the notes for your version before building new plans around it
i
Plan Ahead

Because the future of IPVS mode in kube-proxy is under discussion, treat it as a strong option for existing large clusters rather than an automatic choice for new designs. Read the current release notes and deprecation notices for your Kubernetes version.

7nftables Mode: The Modern Successor to iptables

A newer in-kernel option that keeps the rule-based idea but fixes its biggest scaling weakness.

What nftables is

nftables is the modern replacement for iptables in the Linux kernel. It offers a cleaner way to describe rules and, crucially, supports sets and maps: data structures that let one rule say “if the destination is in this set, jump to the matching target found in this map.” Looking up an entry in a map takes about the same time however large the map is.

Simple Analogy

iptables is a long checklist read from the top. nftables is a phone book: you flip straight to the right name. The checklist gets slower with every added line. The phone book stays about as fast however many names it holds.

What changes for kube-proxy

In nftables mode, kube-proxy keeps its familiar job but expresses Services as map entries instead of long chains. Updates are applied as atomic transactions, so the kernel switches from the old rules to the new ones in one step, with no window in which rules are half-written. This gives performance in large clusters that is much closer to IPVS, without IPVS’s extra kernel modules and its separate connection table. The mode matured through the recent releases and is meant as the long-term in-kernel path on Linux, so check your version’s notes for its current status.

Aspectiptablesnftables
Rule lookup for ServicesWalks chains, cost grows with ServicesMap lookup, cost stays nearly flat
Rule update styleRewrites large rule setsAtomic incremental transaction
Kernel requirementAlmost anyA reasonably modern kernel with nftables support
Operator familiarityVery highGrowing, tools differ
Interaction with other toolsMany tools write iptables rulesSome tools still assume iptables, so check for conflicts
!
Check Your Surroundings

Switching to nftables mode affects more than kube-proxy. Other software on the node that edits iptables rules, such as some security agents or network plugins, may not expect it. Test the combination in a non-production cluster, and confirm that your operating system’s kernel and tools support it.

Why mention it in an iptables-versus-IPVS-versus-eBPF post

Because for many teams in the coming years the real question is no longer “iptables or IPVS,” but “stay with kube-proxy in its newer nftables form, or move to an eBPF-based network plugin that replaces kube-proxy.” Knowing that nftables exists keeps the comparison honest and up to date.

8eBPF: Replacing kube-proxy Altogether

The third approach is different in kind. It does not tune kube-proxy, it retires it.

What eBPF is

eBPF is a technology that lets small, safe programs run inside the Linux kernel at specific hook points, such as when a packet arrives or when a program opens a connection. The kernel checks each program for safety before running it, so it cannot crash the system. Network plugins use eBPF to handle packets with custom logic, without the long rule chains of iptables.

Simple Analogy

Instead of giving the mail room a rulebook to read for every letter, you place a small smart sorting gadget directly at the door of the building. The gadget has the address lookup built in, acts the instant a letter arrives, and was inspected for safety before it was allowed to be installed.

Not a kube-proxy mode, a replacement

This point is important. eBPF is not one of the values you can set in kube-proxy’s mode field. Instead, a network plugin that uses eBPF can take over Service handling completely. Popular plugins offer a kube-proxy replacement feature, and with it enabled you do not run kube-proxy at all. The plugin watches the same Services and EndpointSlices, but keeps its own eBPF tables, usually hash maps, and the eBPF programs consult those maps.

Where the load balancing happens

At connection time, inside the client

For traffic from Pods and processes on the node itself, an eBPF program can attach to the moment a program calls connect. It swaps the Service address for a real Pod address right there, before any packet is created. The packets that follow already carry the Pod address, so there is no per-packet rewriting and no connection-tracking entry for the translation. Replies need no reverse translation either.

At packet time, for outside traffic

For traffic arriving from outside the cluster, such as NodePort or load balancer traffic, programs attached at the network card or just above it can pick a Pod and forward the packet. Some plugins can do this very early, before the normal network stack spends effort on the packet, and can use consistent hashing so that different nodes make the same choice for the same connection.

Direct server return

Some plugins support a mode where the reply goes straight from the chosen Pod back to the client without passing through the node that received the request, which saves a hop for large responses.

flowchart LR
  subgraph IN["Traffic from inside the node"]
    C["Pod calls connect to Service IP"] --> H1["eBPF hook at connect"]
    H1 --> M1[("eBPF service map")]
    M1 --> T1["Destination swapped to a real Pod IP"]
  end
  subgraph OUT["Traffic from outside the cluster"]
    X["Packet arrives at node port"] --> H2["eBPF hook at network device"]
    H2 --> M2[("eBPF service map")]
    M2 --> T2["Packet forwarded to chosen Pod"]
  end
        
FIG 4 — eBPF replacement: Service lookups happen at connect time inside the node, or at the network device for external traffic

Advantages

  • Constant-time lookups using hash maps, so very large clusters stay fast
  • Connect-time translation removes per-packet work for in-cluster traffic
  • Preserves the original client address more easily for external traffic
  • Incremental map updates with no big rule rewrites
  • Rich built-in observability from the same eBPF layer

Disadvantages / Trade-offs

  • Requires a reasonably modern kernel with the needed eBPF features
  • Ties Service handling to one network plugin, which raises switching costs
  • Familiar tools such as iptables listings no longer show the whole story
  • Debugging needs different skills and tools
  • Behaviour with very old applications or special setups may need testing
i
Not Only One Vendor

More than one popular network plugin offers an eBPF data plane with kube-proxy replacement. Features, requirements and maturity differ between plugins and between versions, so read the specific plugin’s documentation for your kernel and cluster version.

9The Journey of a Packet in Each Engine

Put the three approaches side by side by following the same request through each.

The scenario

A front-end Pod on node A opens a connection to a back-end Service. The Service has three ready Pods, one on node A, one on node B and one on node C. We follow the first packet and what happens next, in each engine.

1

iptables mode

The packet leaves the front-end Pod and enters the node’s packet-handling path. It is matched against the top-level service chain, jumps to the Service’s chain, a random pick selects one Pod rule, and the destination is rewritten. The connection tracker records the mapping, so later packets skip the chains.

2

IPVS mode

The packet is addressed to a Service IP that the node considers local. The kernel hands it to IPVS, which looks up the virtual server by hash, applies the chosen scheduling algorithm, picks a real server, and forwards the packet with the destination rewritten. IPVS records the connection in its own table.

3

nftables mode

The packet is matched by a single lookup into a map keyed by Service address and port. The map’s result points to a Pod-selection step, the destination is rewritten, and the connection tracker records it, much like iptables but without the long chain walk.

4

eBPF replacement

When the front-end program calls connect, a hook consults the service map and writes a real Pod address as the destination before the first packet exists. The kernel simply sends packets to that Pod, and nothing later needs rewriting.

sequenceDiagram
  participant F as Front-end Pod on node A
  participant K as Node A kernel
  participant B as Back-end Pod on node B
  Note over F,K: iptables, IPVS or nftables
  F->>K: Packet to Service IP
  K->>K: Match Service, pick Pod, rewrite destination
  K->>B: Packet to Pod IP on node B
  B-->>K: Reply from Pod IP
  K->>K: Reverse rewrite to Service IP
  K-->>F: Reply appears to come from Service IP
  Note over F,K: eBPF connect-time translation
  F->>K: connect to Service IP
  K->>K: Hook swaps in Pod IP before sending
  F->>B: Packets go straight to Pod IP
  B-->>F: Replies need no reverse rewrite
        
FIG 5 — The same request: rewritten per packet with a reverse step, or translated once at connect time

What the comparison teaches

All approaches pick a Pod and rewrite a destination. The difference is when and how. The rule-based engines do it as the packet passes through, and rely on connection tracking to remember the choice and to undo the rewrite on replies. The connect-time eBPF approach does it once at the start for in-node clients, so the rest of the connection is plain. For traffic that arrives from outside the node, all engines must rewrite as the packet passes, so the advantage of connect-time translation applies mainly to in-cluster, same-node origins.

10Side-by-Side Comparison

One table that collects the differences that matter in day-to-day decisions.

AspectiptablesIPVSnftableseBPF replacement
Where it liveskube-proxy modekube-proxy modekube-proxy modeNetwork plugin, no kube-proxy
Service lookup costGrows with Service countNear constant, hash tableNear constant, map lookupNear constant, hash map
Rule or table updatesHeavy, rewrites large setsIncrementalAtomic and incrementalIncremental map updates
Balancing choicesRandomRound robin, least connection, hashing and moreRandom stylePlugin dependent, often hash-based and consistent
Kernel needsMinimalIPVS modulesnftables supportModern kernel with eBPF features
Debugging styleFamiliar, but long rule listsIPVS tools plus some rulesnftables toolseBPF tools and plugin tooling
Fit for small clustersExcellentFine, little gainFineFine, adds dependency
Fit for very large clustersStrainedStrongStrongStrong
Main riskSlowdown with scaleTimeout surprises and uncertain futureTool interactions on the nodePlugin lock-in and kernel needs
!
Read Benchmarks with Care

Published speed comparisons depend heavily on the test hardware, kernel version, number of Services and Pods, and traffic pattern. A result that shows a huge gap in a test with tens of thousands of Services may show almost nothing in a cluster with a few hundred. Always benchmark with your own workload shape.

Two kinds of cost

It helps to separate two costs when comparing. The data path cost is how long it takes to handle each packet or connection. The control path cost is how long and how much CPU it takes to apply a change. A cluster with few, long-lived connections but frequent Pod changes cares mostly about the control path. A cluster with stable Pods but millions of short connections cares more about the data path. The engines differ in both, so identify which one hurts you before choosing.

Data
cost per connection or packet as traffic flows
Control
cost of applying each Service or Pod change
Ops
cost of debugging, upgrading and staffing

11Performance and Scalability in Practice

Where each engine’s costs show up, and what you can do before changing the engine.

Where problems appear first

In rule-based modes, the first visible symptom is usually slow rule programming rather than slow packets. As the rule set grows, each sync takes longer, and during busy periods syncs queue up. The effect is that new Pods take longer to become reachable and removed Pods keep receiving traffic a little longer. CPU use of kube-proxy on every node climbs, and in extreme cases the table update itself becomes a source of short pauses for traffic.

Tuning before switching

Lever 1

Adjust sync timing

Lengthening the minimum interval between syncs reduces CPU spent on rule rewriting, at the cost of slightly slower reaction to changes.

Lever 2

Keep endpoints lean

Services with huge numbers of Pods multiply rule counts. Splitting very large Services, or using topology hints, reduces the work per node.

Lever 3

Avoid needless Service churn

Applications that create and delete Services constantly force constant rule updates. Reuse Services where you can.

Lever 4

Tune connection tracking

A full or too-small connection table causes dropped connections and strange timeouts, so size it for your traffic.

Lever 5

Give kube-proxy enough resources

Starved kube-proxy Pods sync slowly. Set sensible CPU and memory requests for the node agent.

Locality: keeping traffic close

Performance is not only about CPU. Sending a connection to a Pod in another zone adds latency and, on many clouds, cross-zone transfer charges. Newer Kubernetes versions let a Service prefer endpoints in the same zone, or on the same node, and kube-proxy modes honour those hints when programming rules. This can cut both latency and cost, but it must be used with enough Pods in each zone, otherwise a few Pods get overloaded while others sit idle.

Preferring the same zone

With zone-aware hints, each node’s rules favour Pods in its own zone when there are enough of them. If a zone has too few Pods, the hints are dropped, so that traffic is not concentrated on a tiny set of Pods.

Preferring the same node

For per-node agents, the Service can prefer a Pod on the very same node, which removes network hops entirely, with a fallback if no local Pod exists.

i
Measure Before You Migrate

Switching engines is a significant operation. If kube-proxy’s sync time and CPU are modest, and Pods become reachable quickly, the existing mode is probably fine, and the effort is better spent elsewhere. Let measurements, not fashion, drive the decision.

12Traffic Policies, Source IPs and Session Stickiness

The options that change which Pod is chosen and what address the Pod sees.

External traffic policy

For Services reachable from outside, such as NodePort and LoadBalancer types, there is a setting that controls how a node treats incoming traffic. In the Cluster setting, a node may forward the connection to a Pod on any other node, spreading load evenly but adding a hop and usually masking the original client address. In the Local setting, a node only sends traffic to Pods running on itself, which keeps the client address intact and avoids an extra hop, but can create uneven load if Pods are not spread evenly across nodes.

SettingExtra hop?Client address kept?Load evennessNeeds
ClusterPossibleUsually hidden by address masqueradingEven across all PodsNothing special
LocalNoYesDepends on Pod spread per nodeHealth checks so load balancers skip nodes without local Pods
Simple Analogy

Cluster policy is like a reception desk that may pass a visitor to any department in the building, which balances work but means the visitor’s own badge details get replaced by the desk’s. Local policy only lets the desk use staff sitting at that desk, so details stay accurate but a desk with no staff cannot help.

Why the source address matters

Applications sometimes need the real client address for logging, rate limiting or access rules. Address masquerading replaces it with the node’s own address, which hides the client. The Local policy, or an eBPF data plane that avoids masquerading, keeps the original address. This is one practical reason teams move to eBPF or adopt the Local policy for externally exposed Services.

Session affinity

A Service can ask that connections from the same client address keep going to the same Pod for a period of time. All modes support this, but they implement it differently, and it is based only on the client’s network address, not on cookies. Clients behind a shared address, such as a large office network or another proxy, will all stick to one Pod, which can overload it.

Health checks for load balancers

When the Local policy is used with a cloud load balancer, kube-proxy exposes a small health-check port on each node. It answers positively only when the node has a ready local Pod for the Service. The cloud load balancer uses that answer to stop sending traffic to nodes that cannot serve it. Without this, the Local policy would drop connections on nodes without Pods.

!
Uneven Spread Under Local Policy

If one node has three Pods and another has one, the node with three Pods does not receive three times the traffic under common cloud load balancer behaviour. Each node typically gets a similar share, so the lone Pod can be overloaded. Spread Pods evenly with topology rules when you use the Local policy.

13Connection Tracking and Graceful Termination

What happens to connections that are already open when a Pod goes away, and why some mistakes only appear with UDP.

The connection tracking table

The kernel keeps a table of active connections, called the conntrack table. In the rule-based modes it holds the mapping from a Service address to the chosen Pod, so replies can be rewritten back. The table has a fixed size. If it fills up, new connections are dropped, which shows up as mysterious timeouts on busy nodes. Monitoring its usage is as important as monitoring CPU.

Stale entries and UDP

When a Pod behind a Service is removed, entries pointing to it may remain in the table. For TCP, the connection simply breaks and the client reconnects, which picks a live Pod. For UDP, there is no connection to break, so the client’s packets may keep going to the removed Pod’s address until the entry expires, producing a silent black hole. For this reason kube-proxy actively clears stale entries for UDP Services when endpoints change. DNS is a common UDP Service, which is why conntrack problems often show up as intermittent DNS failures.

Simple Analogy

The conntrack table is a notebook where the switchboard writes “caller 17 is talking to agent 4.” If agent 4 leaves, a caller already in conversation hears silence. A phone call ends and the caller redials. A one-way message such as UDP has no call to end, so messages keep being dropped into the empty chair until someone crosses out the notebook entry.

Graceful termination

When a Pod is told to stop, it should keep serving the connections it already has, while no new connections arrive. Modern Kubernetes marks such endpoints as terminating, and kube-proxy modes cooperate: they stop choosing the terminating Pod for new connections but leave existing mappings alone until the Pod is gone. In IPVS mode this is done by lowering the real server’s weight to zero, so it receives nothing new but keeps its current connections.

1

Pod receives a stop signal

It is marked as terminating, and its readiness becomes false.

2

Rules stop sending new connections to it

kube-proxy updates the rules once the watch event arrives. This takes a short moment, during which a few new connections may still arrive.

3

Existing connections finish

The Pod keeps serving them until they end or the grace period expires.

4

Pod is removed

Remaining entries are cleaned up, so no traffic is sent to a Pod that no longer exists.

i
Give Rules Time to Catch Up

Because rule updates take a moment, applications are best configured to keep serving for a few seconds after receiving a stop signal. A short pre-stop delay in the Pod prevents the tiny window where new connections still reach a Pod that is already closing.

14Real-World Choices: Who Uses What and Why

Practical guidance for different cluster shapes.

Small and medium clusters with ordinary traffic

The default iptables mode is usually the right answer. It is available everywhere, widely understood and fast enough when the number of Services is in the hundreds. Changing it brings extra risk with little visible benefit.

Large clusters with thousands of Services

Teams often move to IPVS or, in newer versions, nftables mode, or to an eBPF network plugin. The goal is to stop rule programming time from growing with the Service count and to keep new Pods reachable quickly.

Platforms that need strong observability and security from one layer

Organisations that already adopt an eBPF-based network plugin for visibility and policy often enable its kube-proxy replacement as well, consolidating Service handling, policy and flow visibility in one component.

Environments with very mixed traffic lengths

Where connection durations vary hugely, such as a mix of quick requests and long streaming sessions, IPVS with the least-connection algorithm can give a fairer spread than random choice.

Managed Kubernetes services

Managed platforms usually choose the engine for you, often a default mode or their own network plugin, and may limit what you can change. Check your provider’s documentation to see which modes are supported and whether the engine can be switched on existing node pools.

Windows nodes

Windows nodes use a different kube-proxy implementation tied to the Windows networking stack, so the Linux engines in this post do not apply there. Mixed clusters must consider both.

“Choose the simplest engine that your measurements say you can live with, and change only when the numbers ask you to.”

15Migrating Between Modes Safely

Changing the engine under live traffic is possible, but it rewards careful, gradual steps.

Why migration is delicate

The Service switchboard sits under every connection in the cluster. A mistake can break internal calls between applications, DNS or access from outside. Switching modes replaces one set of kernel structures with another, and leftover rules from the old mode may linger unless they are cleaned up. A staged approach limits the blast radius.

1

Confirm prerequisites

Check kernel version and modules, operating system support and compatibility of any other node software that edits network rules.

2

Rehearse in a non-production cluster

Reproduce your Service count, traffic pattern and failure cases, and measure sync time, latency and error rates before and after.

3

Pick a small canary node pool

Change the mode on a few nodes, usually by rolling them with a new configuration, and keep the rest unchanged. Mixed modes across nodes are generally workable because each node handles traffic leaving it.

4

Watch the right signals

Monitor error rates, connection resets, DNS success, programming latency and conntrack usage on canary nodes versus the rest.

5

Roll out gradually, node pool by node pool

Expand only after each stage looks healthy, and keep a clear rollback path to the previous mode.

6

Clean up old rules

Ensure leftover rules from the previous engine are removed, either by node replacement or by the supported cleanup procedure, so stale rules do not interfere.

Special care for eBPF replacement

Moving to an eBPF plugin that replaces kube-proxy is a larger step than switching modes, because it is often combined with changing or reconfiguring the network plugin itself. Plan it as a network migration project, with a tested rollback, a maintenance window for the riskiest stages, and a clear plan for removing kube-proxy only after the replacement has proven itself. Expect to retrain operators on new tools.

!
Do Not Run Two Owners of the Same Job

Having both kube-proxy and an eBPF replacement trying to handle Services on the same node leads to confusing, duplicated or conflicting behaviour. Each node should have exactly one owner of Service translation at any moment.

16Security Considerations

The component that rewrites every Service connection deserves careful protection.

Privileges of the node agent

kube-proxy edits kernel networking state, so it runs with high privileges on every node, usually with network administration rights. A compromised kube-proxy Pod could redirect traffic, which is powerful. Protect it by restricting who can change its configuration, using trusted images, and limiting its access to the API server to only what it needs. Its identity is normally bound to a narrow role for reading Services and EndpointSlices.

Exposure through NodePorts and external addresses

A NodePort Service opens a port on every node, often across a wide range, whether or not the node runs any of the Service’s Pods. This enlarges the attack surface. Limit where nodes are reachable from with network firewalls and security groups, and restrict which address ranges NodePorts bind to when you do not need them on every interface. For load balancer Services, use source range restrictions so that only expected networks can connect.

!
External IPs on Services

Service definitions can include arbitrary external addresses. If any user may set them, one tenant can attract traffic meant for another address inside the network. Restrict this capability with admission controls, unless there is a clear need for it.

Policy is a different layer

Because kube-proxy does not enforce network policy, a cluster without a policy-capable network plugin has no Pod-to-Pod restrictions at all, however carefully the Services are set up. Network policy and Service translation interact: policy is evaluated against real Pod addresses after translation in rule-based modes, while eBPF plugins implement both together and apply identity-aware rules. Test policies with your chosen engine, since subtle differences exist in how they treat Service traffic.

Information exposure

Metrics and health ports

kube-proxy exposes a metrics endpoint and a health endpoint. Bind them to local or internal interfaces and protect them, because they reveal details of cluster structure.

Rule visibility

Anyone with access to a node can read its rules and learn Service addresses and Pod placement. Limit node access and treat node shells as privileged.

Supply chain for plugins

An eBPF-based plugin loads programs into the kernel with high privilege. Use trusted, version-pinned releases from reputable sources, and review the permissions its Pods require.

17Monitoring, Logging and Troubleshooting

What to watch, and how to find the problem when a Service does not respond.

Metrics worth tracking

Metric idea

Sync duration

How long each rule synchronisation takes. A steady rise points to a growing rule set or an overloaded node.

Metric idea

Network programming latency

The delay from a change in cluster objects to the rules being active on nodes. This reflects what users feel during rollouts.

Metric idea

Last successful sync time

If this stops advancing, kube-proxy is stuck or failing, and rules on that node are going stale.

Metric idea

Rule and entry counts

Total rules or table entries, to see how the data structure grows with the cluster.

Node signal

Conntrack usage

How full the connection table is. Near-full tables lead to dropped connections.

Node signal

kube-proxy restarts and CPU

Frequent restarts or sustained high CPU suggest misconfiguration, missing kernel features or oversized rule sets.

A troubleshooting path for an unreachable Service

1

Confirm the Service has ready endpoints

If the EndpointSlices are empty, the problem is label selectors or readiness checks, not kube-proxy.

2

Reach a Pod directly by its address

If direct connections fail, the issue lies in Pod networking or network policy, not in Service translation.

3

Reach the Service by its virtual address, not its name

If the address works but the name does not, DNS is the culprit. If neither works, translation on the node is suspect.

4

Check kube-proxy on the affected node

Look at its logs and health. Find out whether it is running, whether the mode loaded correctly and whether syncs succeed.

5

Inspect the kernel’s view

Look at the node’s rules, virtual server table or eBPF maps to see whether the Service and its Pods actually appear there, using the tool that fits your engine.

6

Check conntrack and node-level limits

A full table or restrictive firewall rules outside kube-proxy’s control can mimic Service failures.

Typical symptoms and causes

SymptomLikely causeWhere to look
Service works from some nodes but not otherskube-proxy stale or failing on those nodesLast sync time and logs on the affected nodes
Intermittent DNS timeoutsConntrack pressure or UDP stale entriesConntrack usage and drops, cleanup behaviour
New Pods slow to receive trafficSlow rule programmingSync duration and programming latency
After switching to IPVS, nothing worksMissing kernel modules or leftover rulesModule list, kube-proxy startup logs
Backend sees node address instead of clientMasquerading under the Cluster policyTraffic policy and plugin settings
Long idle connections drop in IPVSIPVS connection timeoutsTimeout settings and keepalive use
i
Logging with Purpose

Raise log verbosity only while investigating, because detailed logs are very large. Record mode changes and configuration edits with who made them and when, so you can connect a behaviour change to a cause.

18Design Patterns and Best Practices

Habits that keep Service networking boring, which is exactly what you want.

Patterns that work

Start simple, measure, then change

Run the default mode, publish dashboards for sync time and programming latency, and only consider another engine when those numbers cross a threshold you have agreed in advance.

Use readiness checks that mean something

Because readiness decides when a Pod enters the rules, make sure it reflects true ability to serve. A weak check sends traffic to Pods that cannot handle it, and a failing check can cause flapping and rule churn.

Add a pre-stop pause

Let Pods continue serving briefly after being told to stop, so that rule updates have time to reach all nodes before the Pod disappears.

Spread Pods across nodes and zones

Even spread makes the Local traffic policy and zone-aware routing work well and reduces the blast radius of a node failure.

Standardise the node image

Keep kernel version and required modules consistent across nodes, so that switching or tuning modes behaves predictably everywhere.

Version-control the proxy configuration

Treat kube-proxy settings as reviewed, versioned configuration and roll changes out gradually, never by editing a live node.

Do

  • Monitor sync time, programming latency and conntrack usage
  • Test any mode change on a canary node pool first
  • Size the connection tracking table for your peak traffic
  • Keep kernel, modules and kube-proxy versions aligned
  • Use topology-aware options to keep traffic local when possible
  • Restrict NodePort and external address exposure

Do not

  • Switch engines because of a benchmark you did not reproduce
  • Assume kube-proxy enforces network policy
  • Run kube-proxy and an eBPF replacement together on the same node
  • Ignore UDP and DNS when planning conntrack capacity
  • Leave Services with huge endpoint counts untuned
  • Expose NodePorts broadly without a firewall in front

19Common Mistakes and Anti-Patterns

Failures seen again and again in real clusters.

ANTI-PATTERN-01 Avoid
Problem

The team switches every node to IPVS at once because an article said it is faster, without checking kernel modules.

Why It’s Harmful

Nodes missing the needed modules fail to program Services correctly, and traffic between applications breaks cluster-wide within minutes.

Correct Approach

Verify prerequisites on every node image, rehearse in a test cluster, and roll out in stages from a small canary pool with a rollback ready.

ANTI-PATTERN-02 Avoid
Problem

Conntrack table size is left at defaults on nodes that handle heavy DNS and short-lived connections.

Why It’s Harmful

When the table fills, new connections are dropped. Symptoms look like random application errors and DNS timeouts, and no single log line explains them.

Correct Approach

Size the table for peak load, watch its usage as a standard node metric, and alert well before it fills.

ANTI-PATTERN-03 Avoid
Problem

Applications cache the resolved address of a Service forever or store individual Pod addresses.

Why It’s Harmful

Pods change constantly, so cached Pod addresses go stale. Even a cached Service address can misbehave if the Service is deleted and recreated with a new address.

Correct Approach

Connect by Service name, honour DNS time-to-live values, and reconnect gracefully after failures.

ANTI-PATTERN-04 Avoid
Problem

Local traffic policy is turned on for an externally exposed Service whose Pods sit unevenly across nodes.

Why It’s Harmful

Nodes with few Pods receive a similar share of traffic to nodes with many, so some Pods are overloaded while others idle, and nodes with no Pods may be sent traffic if health checks are misconfigured.

Correct Approach

Spread Pods evenly with topology rules, make sure load balancer health checks use the node health port, and watch per-Pod load.

ANTI-PATTERN-05 Avoid
Problem

Operators hand-edit firewall or packet rules on nodes to “fix” a Service problem.

Why It’s Harmful

kube-proxy owns its rules and will rewrite or conflict with manual changes, and other node software may also edit the same tables. The result is rules that work until the next sync and then break again.

Correct Approach

Fix the cause at its source, whether configuration, labels or policy, and let kube-proxy or the network plugin own the rules for Services.

ANTI-PATTERN-06 Avoid
Problem

Teams blame kube-proxy for blocked traffic that is really a network policy, a firewall or a security group.

Why It’s Harmful

Hours go into inspecting Service rules, while the actual block sits in a different layer and stays untouched.

Correct Approach

Follow the troubleshooting path from endpoints to direct Pod reachability to Service address to node rules, so each layer is ruled in or out in turn.

20Advantages, Disadvantages and Trade-offs

A final balance sheet for the whole kube-proxy approach and its alternatives.

Advantages of kernel-level Service handling

  • No extra hop and no central bottleneck, since every node translates locally
  • Works without changing applications, which just use a name
  • Survives kube-proxy restarts because rules live in the kernel
  • Choice of engines to fit cluster size and operating style
  • Rich integration with traffic policies, topology hints and graceful termination
  • eBPF options add observability and avoid per-packet rewriting for in-node clients

Disadvantages / Trade-offs

  • Every node must hold the state for every Service, costing memory and sync work
  • Connection-level balancing only, so requests on one long connection stay on one Pod
  • Rule-based engines struggle at very large scale
  • Debugging requires kernel-level skills that differ per engine
  • Engine changes affect the whole cluster’s networking
  • eBPF replacement adds plugin dependency and kernel requirements

Connection-level balancing is not request-level

A subtle limit worth repeating: kube-proxy engines balance new connections, not individual requests. If a client opens one long-lived connection, such as with some modern protocols that multiplex many requests, all those requests go to the same Pod. Teams that need request-level balancing, retries or fine-grained traffic shaping turn to ingress controllers, gateways or service meshes on top of Services, not instead of them.

Choosing between the options

SituationBest fitWhy
Small or medium cluster, no performance complaintsiptablesSimple, universal, proven
Large cluster on a kernel with IPVS support, needs balancing choicesIPVS, with awareness of its futureConstant-time lookups and algorithm choice
Large cluster on a recent Kubernetes version, wants to stay with kube-proxynftablesScales well and is the modern in-kernel path
Wants visibility, identity-aware policy and the lowest overhead from one layereBPF kube-proxy replacementSingle data plane, connect-time translation
Managed cluster with limited controlProvider’s supported optionSupportability matters more than theoretical speed

21Frequently Asked Questions

Questions that beginners and experienced engineers keep asking.

Q1Does traffic actually flow through the kube-proxy program?

In the modes used today, no. kube-proxy only programs the kernel, and the kernel moves the packets. If kube-proxy stops, existing rules keep working with their last known state, though updates stop until it returns.

Q2Is eBPF a mode I can set in kube-proxy?

No. eBPF-based Service handling comes from a network plugin that replaces kube-proxy. You enable the plugin’s replacement feature and do not run kube-proxy on those nodes.

Q3Why does a ClusterIP not answer a ping?

Because the address is virtual and the rules usually match only the specific protocol and port of the Service. A ping uses a different protocol, so nothing translates it. Test with a real connection to the Service port instead.

Q4Can different nodes run different modes?

Generally yes, since each node translates traffic that leaves it, which is why gradual migration by node pool works. It does make troubleshooting harder, so keep mixed periods short and well labelled.

Q5Do existing connections break when I change modes?

They can. Mappings live in kernel tables owned by the old engine, so switching may drop or orphan some connections. Roll nodes gradually, drain them when possible, and expect clients to reconnect.

Q6Is IPVS always faster than iptables?

Not always. In small clusters the difference is usually invisible. The advantage appears when the number of Services is large and rule programming time dominates. Benchmark your own workload.

Q7Why do I see node addresses instead of client addresses in my logs?

Under the Cluster traffic policy, nodes mask the client address when forwarding to a Pod on another node. Use the Local policy or an eBPF data plane that avoids masquerading if you need real client addresses.

Q8What breaks first as a cluster grows?

In rule-based modes, usually rule programming time and CPU use of kube-proxy, which shows up as slow reachability of new Pods. Packet speed degrades later. Connection tracking limits are another early casualty on busy nodes.

Q9Does kube-proxy handle headless Services?

No. Headless Services have no virtual address, so DNS returns Pod addresses directly and clients connect to Pods without any Service translation.

Q10Does kube-proxy load balance individual requests?

No. It balances new connections. Many requests on one connection all go to the same Pod. For request-level balancing, use an ingress, gateway or service mesh layer.

Q11Which mode should a new cluster choose today?

Start from what your platform supports and what the current release notes recommend. Many teams begin with the default, then consider nftables or an eBPF plugin if they need more scale or visibility. Avoid building new plans around a mode that your version’s notes mark as deprecated.

Q12Is it safe to restart kube-proxy during the day?

Usually yes, because existing rules stay in the kernel while it restarts. The risk is the gap during which Service changes are not applied, and the full resync on startup, so restart nodes gradually rather than all at once.

22Summary and Key Takeaways

Pods are temporary, so Kubernetes gives groups of them a stable virtual address through Services, and keeps the list of ready Pods in EndpointSlices. kube-proxy watches those objects on every node and writes instructions into the kernel so that traffic to a Service address is redirected to a healthy Pod. In the rule-based modes the kernel does the rewriting, kube-proxy only programs it, and connection tracking remembers each choice. The iptables mode is simple and universal but slows as the number of Services grows. IPVS uses hash tables and offers several balancing algorithms but needs extra kernel pieces and has an uncertain future. nftables keeps the in-kernel model but uses maps for constant-time lookups and atomic updates. eBPF replacement, provided by network plugins, removes kube-proxy and translates Services at connect time or at the network device.

Come back to the helpline at the start. The number stays fixed, the agents come and go, and the switchboard keeps its list current. What differs between the engines is how that switchboard is built: a long checklist, an indexed machine, a phone book or a smart gadget at the door. The best choice is the simplest one that your measurements show you can live with, adopted gradually, watched carefully and understood well enough to debug at three in the morning.

Key Takeaways

  • kube-proxy programs, the kernel forwards — it translates Services and EndpointSlices into kernel instructions and does not carry your packets in modern modes.
  • iptables is the safe default — universal and well understood, but rule matching and rewriting grow with the number of Services, which hurts at large scale.
  • IPVS and nftables fix the scaling curve — hash and map lookups with incremental or atomic updates, with IPVS offering more balancing algorithms and nftables avoiding extra kernel modules.
  • eBPF replaces rather than tunes kube-proxy — a network plugin keeps its own maps, translates at connect time or at the network device, and brings observability along with plugin dependency.
  • Separate data path from control path — decide whether packet handling or rule programming is your real bottleneck before choosing an engine.
  • Mind the edges — traffic policy, source addresses, session affinity, conntrack size and graceful termination cause more real incidents than the engine itself.
  • Migrate gradually and measure — canary a node pool, watch sync time, programming latency and conntrack, keep one owner of Service translation per node, and keep a rollback ready.