What is Load Balancer?
A load balancer evenly distributes incoming traffic among web servers or workers defined in a load-balanced pool. Users connect to the public IP / VIP of the load balancer; backend servers are reachable over private IP from the load balancer only. Examples: Nginx, HAProxy, Amazon ELB/ALB/NLB, F5, Cloudflare.
Block Diagram — Load Balancer Placement
The client never talks to individual web servers directly. DNS resolves the domain to the load balancer's virtual IP (VIP). The LB terminates or forwards the connection, picks a healthy backend from its pool, and rewrites the destination address before forwarding on the private network.
flowchart TB
subgraph Internet
C[Client / Browser
DNS: example.com → 1.2.3.4]
end
C -->|HTTP/TCP to VIP 1.2.3.4| LB
subgraph DC["Data Center / VPC — private network"]
LB[Load Balancer
VIP: 1.2.3.4 public
10.0.0.1 private]
subgraph Pool["Backend pool"]
WS1[Web Server 1
10.0.1.10:8080]
WS2[Web Server 2
10.0.1.11:8080]
end
LB -->|forward to chosen backend| WS1
LB -->|forward to chosen backend| WS2
WS1 --> Cache[(Cache cluster)]
WS2 --> Cache
Cache --> DB[(DB farm)]
end
Advantages of LB
1. Failover
If Web-Server1 goes offline, all the traffic will be routed to Web-Server2. This prevents the website from going offline. If traffic grows rapidly and two servers are not enough, new web servers can be added to the pool without changing the public VIP the clients use.
Places where LB can be kept
| Place | Description |
|---|---|
| Between Client (browser) and Web server | LB sends each request to a free / least-loaded web server. |
| Between App-servers and cache | Hundreds of cache servers may need load balancing. |
| Between cache and DB servers | Read replicas or sharded DB nodes can sit behind an LB. |
Scheduling Algorithms for LB
| Name | Description |
|---|---|
| 1. Round Robin | Send request one after the other in rotation. |
| 2. Least Connections | Sends requests to the server with the lowest number of active connections. |
| 3. Least time | Sends requests to the server selected by a formula combining fastest response time and fewest active connections. |
| 4. Hash | Distributes requests based on a key you define, such as client IP or request URL. |
| 5. IP HASH (HTTP only) | Distributes requests based on the first three octets of the client IP address. |
| 6. Random with 2 Choices | Picks two servers at random and sends the request to the one with fewer active connections. |
| 7. DNS Round Robin with Load Balancing | Assign multiple IPs (IP1, IP2, IP3) to one domain. DNS responds with one IP in rotation; each returned IP moves to the end of the list so traffic is spread evenly. |
| 8. Sub domain DNS Delegation with Round Robin | Multiple subdomains (hr.example.com, payroll.example.com) have their own nameservers; the primary nameserver forwards queries to the correct subdomain NS. |
| 9. Client side random load balancing | Deliver a list of server IPs to the client; the client randomly picks one per connection. Assumes all clients generate similar load. |
| 10. Server side load balancers | LB listens on a port, forwards the request to all backend servers, and whichever responds first serves the client. |
| 11. IP ADDRESS based | If the client IP is in range x–y, forward to backend-1; if in range a–c, forward to backend-2, and so on. |
| 12. Layer 5 Aware | Inspects Layer-5 session data (e.g. HTTP headers) to decide which backend receives the packet. |
Types of LB
| Type | Description |
|---|---|
| ALB / Layer-7 / Application LB / Reverse Proxy | LB inspects Layer-7 application data (HTTP/HTTPS headers, URL path, cookies) and routes based on that content. |
| Layer 4 LB / Transport Layer LB |
Operates at the transport layer (TCP, UDP, TLS). Packet is routed
based on source/destination IP and ports without parsing
application payload. Problem: Small Sized HTTP Response via LB Issue: Incoming request passes via LB and response is also sent via LB. If the response is small (2 MB) and the LB is serving larger requests, this small response has to wait unnecessarily. Solution — DSR (Direct Server Return): backends answer directly to the clients without passing the response back through the LB. |
| Layer 3 LB / NLB (Network LB) / VPN LB | Routing decision is made from network-layer headers (source and destination IP). See the dedicated Layer 3 section below. |
| IPVS (IP Virtual Server) | Linux kernel feature that acts as a Layer-4 load balancer — a built-in tool within the Linux kernel for high-speed packet forwarding. |
Layer 3 Load Balancer (NLB, VPN concentrators, anycast edge routers)
A Layer-3 (network-layer) load balancer makes routing decisions using IP headers only. It does not inspect TCP ports in depth (that is Layer 4) and never parses HTTP (Layer 7). It is commonly used for NLB, VPN concentrators, and anycast edge routers that steer flows before transport termination.
How L3 Load Balancer Routes Traffic (Decision Path)
Consider an HTTP request arriving at load balancer with two web
servers(WS1, WS2) behind it. The LB must pick WS1 or WS2 in well under
a microsecond.
LB does not query a database on every request — it keeps hot-path
state in memory.
At starting LB keeps on checking health of Web servers and maintains
the state of Web servers
struct BackendServer {
int backendId;
string ip; // Real IP of web server
int port; // Real Port of web server
int weight;
int maxConnections;
string availabilityZone;
vector <string> tags;
enum class HealthState {UP, DOWN, DRAIN} health_state;
};
unordered_map <int, BackendServer> backendPool;
Steps:
1. Client completes TCP handshake with the LB VIP (SYN → SYN-ACK →
ACK). LB stores every HTTP Client information in
Connection Table.
|
|
2. TLS termination (if HTTPS): LB decrypts using session keys cached
in an SSL session cache
3. LB opens (or reuses from a connection pool) a TCP connection
to WS2 10.0.1.11:8080 and forwards the HTTP request.
4. Response travels back through the LB to the client; connection
table entry is updated or removed when the connection closes.
Why modern load balancers are fast
No disk on the hot path — every lookup is RAM-only.
Kernel bypass — DPDK, XDP/eBPF, or hardware ASICs process
packets in user space or NIC firmware, avoiding syscalls per
packet.
Pre-warmed backend connections — HTTP keep-alive and TLS
session resumption remove repeated handshakes.
Health checks off the request path — probe results are cached;
the forwarding thread only reads a boolean flag.
Capacity — Hardware Specs and Load Metrics
| Profile | Hardware / form factor | Typical sustained load |
|---|---|---|
| Small software LB (1 vCPU VM, nginx/HAProxy) | 1 vCPU, 2 GB RAM, 1 Gbps NIC | ~5k–20k req/s (small HTTP), ~10k concurrent connections |
| Mid-tier software LB (8 vCPU, DPDK) | 8 vCPU, 16 GB RAM, 10 Gbps NIC | ~100k–500k L4 connections/s, ~1–3 M concurrent flows |
| Hardware ADC (F5, A10 class) | Dual CPU, 32–64 GB RAM, FPGA/ASIC assist, 40–100 Gbps | ~1–10 M concurrent connections, multi‑million L7 req/s with SSL offload |
| Cloud NLB / ALB (managed) | Hyperscaler auto-scaled data plane (opaque) | Scales to millions of req/s; limits published per account/region (e.g. ALB pre-warming for sudden spikes) |
Load metrics to watch
| Metric | What it tells you |
|---|---|
| Requests or connections per second | Headroom vs licensed / tested maximum |
| Concurrent connections | Memory pressure on connection table (each flow ≈ hundreds of bytes–KB) |
| P99 latency at LB |
Queueing inside LB when CPU or NIC is saturated P95 means 95% of requests complete at or below this time P99 means 99% of requests complete faster |
| CPU utilization per core | TLS and L7 parsing are CPU-heavy; imbalance suggests sharding needed |
| NIC bandwidth (Gbps) and PPS | Small-packet DDoS or microservice chatter can hit PPS limit before bandwidth cap |
| SSL handshakes per second | Often the first bottleneck unless session resumption is high |
| Backend healthy count | Traffic concentrates on fewer nodes when health checks fail |
Scaling the Load Balancer Itself
Important distinction: scaling backend web servers (adding WS3, WS4 to the pool) is separate from scaling the load balancer tier. When LB capacity is exhausted, you do not magically spawn a new LB instance from a single hardware box — you add redundancy and capacity through architecture.
What gets provisioned when load grows?
| Deployment style | How LB capacity is added | Pod vs standalone? |
|---|---|---|
| Software LB (nginx, HAProxy, Envoy) on Kubernetes | Horizontal Pod Autoscaler increases replica count of the LB Deployment; each pod shares the VIP via kube-proxy, MetalLB, or an external cloud LB. | New pods in the same cluster — not new physical machines unless the cluster itself scales nodes. |
| Software LB on VMs | Add another VM running the same LB software; place both behind ECMP, DNS round-robin, or anycast VIP. | New standalone VM provisioned (second LB instance). |
| Hardware ADC (F5, Citrix) | Add a second physical appliance in active/standby or active/active cluster; license and sync config. | New standalone machine — dedicated appliance, not a pod. |
| Cloud managed (AWS ALB/NLB, GCP LB) | Provider auto-scales opaque data-plane nodes; you may request pre-warming for sharp spikes. No pod you manage. | Internal scaling is hidden; you do not create pods yourself. |
Backends scale out independently: auto-scaling groups or Kubernetes HPA add application pods/VMs and register them in the LB's backend pool via service discovery (Consul, Kubernetes Endpoints, cloud target groups). The LB configuration picks up new backends from its pool table — it does not "birth" them itself.
Primary and Secondary Load Balancer — Communication
Production systems almost never run a single LB. Two (or more) instances share a Virtual IP (VIP) or anycast address so clients see one entry point while operators get redundancy.
Active / Standby (Primary + Secondary)
flowchart LR
C[Clients] --> VIP[VIP 1.2.3.4]
VIP --> P[Primary LB
ACTIVE
holds VIP via VRRP]
VIP -.->|VIP floats on failover| S[Secondary LB
STANDBY
syncs config]
P --> B1[Backend pool]
S -.->|takes over on failure| B1
P <-->|VRRP/keepalived heartbeats
config sync| S
- VIP ownership — protocols like VRRP (keepalived) or cloud failover APIs assign the VIP to the primary. Heartbeats flow over a dedicated link; if the primary stops responding, the secondary gratuitously announces the VIP via ARP/NDP.
- Config sync — primary pushes nginx/HAProxy config, certificates, and routing rules to secondary (rsync, git ops, or vendor cluster sync). Both must have identical backend pools.
- Connection state — by default the standby has an empty connection table. Existing TCP sessions break on failover unless connection sync (HAProxy peers, F5 mirroring) replicates the 5-tuple table in real time — expensive and often limited to short flows.
- Split-brain prevention — quorum witness, STONITH, or cloud API ensures only one node holds the VIP.
Active / Active
Both LBs serve traffic simultaneously. Clients reach either node via:
- DNS round-robin / anycast — same VIP announced from multiple PoPs; BGP anycast routes each client to the nearest healthy LB.
- ECMP — top-of-rack switches hash flows across multiple LB NICs.
Each node maintains its own connection table. Stickiness must use a deterministic algorithm (source-IP hash or cookie) so the same client tends to hit the same node, or stickiness is abandoned in favor of stateless backends.
Session Stickiness (Persistence)
Session stickiness forces requests from the same client (or session) to reach the same backend across multiple requests, even when the LB would otherwise round-robin.
When stickiness helps
| Scenario | Why stickiness matters |
|---|---|
| Shopping cart / in-memory session | Server stores cart in local RAM; without stickiness the cart appears empty on another node. |
| WebSocket or SSE long-lived connection | Connection is pinned to one backend for its lifetime anyway; stickiness ensures reconnect lands on a node that can resume (or use shared store). |
| Legacy apps without shared session store | Avoids rewriting the app to use Redis for sessions. |
| File upload chunked across requests | Chunks must land on the same worker holding partial state. |
When stickiness hurts or is unnecessary
- Stateless REST / gRPC APIs with JWT and shared DB — stickiness adds imbalance when one client hogs a hot server.
- Mobile clients behind carrier NAT — thousands of users share one public IP; source-IP stickiness overloads one backend.
- Backend drain / deploy — sticky sessions resist graceful shutdown unless the LB supports slow drain and cookie refresh.
Internal logic — common mechanisms
| Method | How it works internally |
|---|---|
| Insert cookie (LB-generated) |
On first response LB sets
Set-Cookie: SERVERID=ws2; HttpOnly. On later requests
LB reads the cookie, looks up ws2 in the backend
table, bypasses round-robin. Stored in stickiness hash map with
TTL.
|
| Application cookie (passive) |
LB hashes an existing cookie (e.g. JSESSIONID) with
consistent hashing → backend id. No new cookie needed; works if
the app already sets one.
|
| Source IP hash |
backend = hash(client_ip) mod N. Stateless, fast, but
poor distribution behind NAT.
|
| TLS session ID / ticket | Hash of SSL session id maps to the node that terminated the first handshake — mainly for reconnect optimization. |
| Header-based |
Route by X-User-Id or tenant id hash — useful in
multi-tenant SaaS.
|
Stickiness + multiple LB nodes
In active/active, an inserted cookie must be honored by all LB instances (same cookie name → same backend mapping algorithm). If only the primary knows the table, failover breaks sessions. Best practice: make backends stateless with a shared session store (Redis) and treat stickiness as an optimization, not correctness.