A load balancer distributes incoming network traffic across multiple backend servers to ensure no single server bears too much demand. It improves responsiveness, availability, and resource utilization.
┌─────────────────┐
│ Load Balancer │
└────────┬────────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
[Server 1] [Server 2] [Server 3]
CPU: 30% CPU: 45% CPU: 35%
Without Load Balancer With Load Balancer
Single point of failure High availability
Limited throughput Scales horizontally
Uneven resource use Even distribution
Downtime during deploys Zero-downtime deploys
Request 1 → Server A
Request 2 → Server B
Request 3 → Server C
Request 4 → Server A (cycle repeats)
How : Cycles through servers sequentially
Pros : Simple, fair distribution
Cons : Ignores server capacity/load
Use when : All servers have equal capacity
Server A (weight=5): gets 5/10 requests
Server B (weight=3): gets 3/10 requests
Server C (weight=2): gets 2/10 requests
How : Assigns weights based on server capacity
Pros : Accounts for different server specs
Cons : Weights are static, don’t reflect real-time load
Use when : Servers have different capacities
Server A: 10 active connections → pick
Server B: 25 active connections
Server C: 15 active connections
How : Routes to the server with fewest active connections
Pros : Adapts to real-time load
Cons : Doesn’t account for connection duration
Use when : Requests have varying processing times
Server A: avg 50ms → pick (fastest)
Server B: avg 120ms
Server C: avg 80ms
How : Routes to the server with lowest average response time
Pros : Accounts for both load and network latency
Cons : Can oscillate, more overhead to track
Use when : Response time is critical
hash(client_ip) % num_servers = server_index
Client 192.168.1.1 → hash → Server A (always)
Client 192.168.1.2 → hash → Server B (always)
How : Hash of client IP determines server
Pros : Same client always goes to same server (sticky)
Cons : Uneven distribution if IPs are clustered
Use when : Session affinity is needed without cookies
┌──────────────────┐
│ Hash Ring │
│ ┌──A──┐ │
│ ╱ ╲ │
│ S1 S2 │
│ ╲ ╱ │
│ └──B──┘ │
└──────────────────┘
Request hashes to point on ring → nearest server clockwise
How : Maps both servers and requests to positions on a hash ring
Pros : Minimal redistribution when servers are added/removed
Cons : More complex to implement
Use when : Distributed caching, consistent routing
Algorithm Load Aware Session Affinity Complexity Best For
Round Robin ❌ ❌ Low Equal servers
Weighted RR Partial ❌ Low Unequal capacity
Least Conn ✅ ❌ Medium Varying request times
Least RT ✅ ❌ Medium Latency-sensitive
IP Hash ❌ ✅ Low Session persistence
Consistent Hash ❌ ✅ High Distributed cache
Client → L4 LB → Backend
(TCP/UDP level, no content inspection)
Operates on : IP address + port
Speed : Very fast (minimal processing)
Decisions : Based on network info only
Use cases : Database load balancing, gaming servers
Examples : AWS NLB, HAProxy (TCP mode), Linux IPVS
Client → L7 LB → Backend
(HTTP headers, URL, cookies, content)
Operates on : HTTP headers, URL, cookies, payload
Speed : Slower (must parse HTTP)
Decisions : Content-based routing
Use cases : Web applications, API gateways
Examples : AWS ALB, Nginx, Envoy, HAProxy (HTTP mode)
Feature L4 L7
Speed Faster Slower
Content inspection No Yes
URL-based routing No Yes
SSL termination Pass-through Can terminate
Cookie handling No Yes
WebSocket support Yes (TCP) Yes (HTTP upgrade)
Cost Lower Higher
/api/v1/users/* → User Service
/api/v1/orders/* → Order Service
/static/* → CDN / Static Servers
*.websocket → WebSocket Servers
Health checks ensure traffic is only sent to healthy servers.
Type How Detects
TCP Can we connect to port? Server down
HTTP GET /health returns 200? App crash, dependency failure
Custom Checks DB, cache, disk Deep health issues
Interval: 10 seconds (check every 10s)
Timeout: 5 seconds (mark unhealthy if no response in 5s)
Threshold: 3 (3 consecutive failures → unhealthy)
Recovery: 2 (2 consecutive successes → healthy)
// GET /health
{
"status": "healthy",
"checks": {
"database": "ok",
"cache": "ok",
"disk": "ok"
},
"uptime": 86400
}
Best Practices :
Use shallow health checks for load balancing (fast)
Use deep health checks for monitoring (comprehensive)
Include dependency status in health endpoint
Don’t make health checks too expensive
Sticky sessions (session affinity) ensure a user’s requests go to the same server.
Method How Pros Cons
Cookie-based LB sets cookie with server ID Reliable Requires L7
IP-based Hash client IP Works at L4 IP changes break it
App-level App tracks session Flexible App complexity
Uneven load distribution
Server failure loses all sessions
Harder to scale down
Violates stateless architecture principle
Better Alternative : Store sessions in external store (Redis)
Client → LB → Any Server → Redis (session store)
Solution Type L4/L7 Use Case
Nginx Software L7 (+ L4) Web server + LB
HAProxy Software L4 + L7 High-performance LB
Envoy Software L4 + L7 Service mesh proxy
AWS ALB Managed L7 HTTP/HTTPS workloads
AWS NLB Managed L4 TCP/UDP, ultra-low latency
Cloudflare CDN + LB L7 Global load balancing
F5 Hardware L4 + L7 Enterprise on-prem
┌──────────────┐
│ DNS-based │ (geo-distributed)
│ Global LB │
└──────┬───────┘
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│Regional │ │Regional │ │Regional │
│L4 LB │ │L4 LB │ │L4 LB │
└────┬────┘ └────┬────┘ └────┬────┘
┌──┴──┐ ┌──┴──┐ ┌──┴──┐
▼ ▼ ▼ ▼ ▼ ▼
[App] [App] [App] [App] [App] [App]
Start with requirements — “What’s the traffic pattern? Read-heavy or write-heavy?”
Choose algorithm based on use case — Don’t default to round robin
Explain the “why” — “Least connections because request processing times vary”
Consider failure scenarios — “What happens when a server goes down?”
Mention health checks — Always include health checking in your design
Discuss sticky sessions trade-off — “We’d prefer stateless, but if needed…”
Think about SSL termination — Where does TLS end?
Consider multi-region — “For global users, we’d use DNS-based geo-routing”
❌ Forgetting health checks
❌ Using sticky sessions without discussing trade-offs
❌ Not considering the load balancer as a single point of failure
❌ Ignoring SSL/TLS termination
❌ Choosing L7 when L4 would suffice (adding unnecessary latency)