Speed Over Smarts
Think of Layer 4 load balancing like a postal sorting machine that reads only the address on the envelope — it never opens the envelope to see what's inside. It moves mail fast because it doesn't care about the contents.
Layer 4 (Transport Layer) load balancing operates at the level of TCP/UDP packets. Here is the exact sequence of events:
- A client sends a TCP SYN packet (the first step of a TCP handshake) to the load balancer's Virtual IP (VIP) — a single public IP address that represents the entire backend pool.
- The LB inspects only the Source IP, Destination IP, Source Port, and Destination Port — the four-tuple that identifies a connection. It reads nothing else.
- It selects a backend server using its configured algorithm (e.g., round robin) and performs NAT (Network Address Translation): it rewrites the Destination IP from the VIP to the private IP of the chosen backend server, then forwards the packet.
- The backend server processes the request and responds. The LB (or a return path mechanism) translates the Source IP back so the client sees the VIP as the responder.
Characteristics:
- "Dumb" by design: The LB never opens the packet payload. It has no idea whether you are requesting a video, a login page, or a database query. It does not decrypt TLS.
- Extremely fast: Because it only modifies IP headers and forwards packets, L4 balancing can handle millions of packets per second with microsecond-level latency. The CPU cost per packet is tiny.
- Stateful per connection: The LB must maintain a connection table mapping each client four-tuple to a chosen backend, so all packets in a single TCP session go to the same server.
- Examples: Linux IPVS (used inside Kubernetes kube-proxy), AWS Network Load Balancer (NLB), Google Cloud Network Load Balancer.
The statefulness requirement is a subtle but important constraint. Each L4 LB instance must remember which backend it assigned to each active connection. In a clustered LB setup (multiple LB instances for redundancy), all instances must share this connection table — or you must use a technique like consistent hashing on the four-tuple to ensure the same LB instance always handles the same connection without needing shared state.