Round robin is the default. It’s also often wrong. Here’s how to choose load balancing strategies that actually match your workload.
The Strategies
Round Robin
Each request goes to the next server in rotation.
| |
Good for: Stateless services, similar server capacity
Bad for: Long-running connections, mixed server specs, sticky sessions
Weighted Round Robin
Same rotation, but some servers get more traffic.
| |
Good for: Mixed hardware, gradual rollouts
Bad for: Dynamic load patterns
Least Connections
Send to the server with fewest active connections.
| |
Good for: Variable request duration, long-running requests
Bad for: Quick requests (overhead of tracking connections)
IP Hash (Source Hashing)
Same client always hits same server.
| |
Good for: Session affinity without cookies
Bad for: Uneven client distribution, NAT (many clients share IP)
Consistent Hashing
Hash-based routing that minimizes disruption when servers change.
| |
Good for: Caching layers, stateful services
Bad for: Highly dynamic server pools
Least Time (Response-Based)
Route to fastest responding server.
| |
Good for: Heterogeneous backends, geographic distribution
Bad for: Requires NGINX Plus or HAProxy
Random with Two Choices
Pick two servers randomly, send to less loaded one.
| |
Good for: Large server pools, avoiding hotspots
Bad for: Small pools (overhead isn’t worth it)
Health Checks
A load balancer is only as good as its health checks.
Passive Health Checks
Track failures from real traffic:
| |
3 failures → server marked down for 30 seconds.
Problem: Slow detection. Users hit errors before server is removed.
Active Health Checks
Proactively test servers:
| |
Better: Catches issues before users do.
Deep Health Checks
Check more than “is it responding”:
| |
Best: Removes servers that are up but not useful.
Session Persistence
Sometimes you need requests to stick to a server.
Cookie-Based
Load balancer sets a cookie:
| |
Pros: Works through NAT, survives IP changes
Cons: Requires cookie support
Source IP
Hash client IP:
| |
Pros: No cookies needed
Cons: NAT breaks it, mobile clients change IPs
Application-Level
Let the app handle it:
| |
Best: Stateless servers, sessions in shared store. No sticky sessions needed.
Draining and Graceful Shutdown
Don’t kill connections mid-request.
Connection Draining
Stop new connections, let existing ones finish:
| |
Or in HAProxy:
| |
Graceful Shutdown
Application signals readiness to stop:
| |
Layer 4 vs Layer 7
Layer 4 (TCP/UDP)
Routes based on IP and port. Doesn’t inspect content.
| |
Good for: Databases, non-HTTP protocols, raw performance
Bad for: HTTP features (path routing, headers, cookies)
Layer 7 (HTTP)
Inspects HTTP content. Routes based on path, headers, etc.
| |
Good for: HTTP services, content-based routing
Bad for: Raw TCP, protocols that aren’t HTTP
Common Mistakes
1. Ignoring Connection Limits
| |
2. No Timeouts
| |
3. Missing X-Forwarded Headers
| |
4. One Massive Pool
| |
Choosing a Strategy
| Workload | Strategy |
|---|---|
| Stateless, similar servers | Round robin |
| Mixed server specs | Weighted round robin |
| Variable request duration | Least connections |
| Need session affinity | IP hash or sticky cookies |
| Caching layer | Consistent hash |
| Large pool, want fairness | Random two choices |
| Geographic distribution | Least time (response-based) |
Start with round robin. Move to least connections if you have variable request times. Add health checks always.
Load balancing is the art of spreading work without spreading pain. Choose wisely, monitor constantly.