Nginx Load Balancing Algorithms
Nginx provides multiple load balancing algorithms to distribute traffic across upstream servers based on different criteria. Selecting the appropriate algorithm depends on your application requirements, session state, server capabilities, and traffic patterns. This guide covers all available algorithms, configuration examples, health checks, keepalive connections, and best practices.
Table of Contents
- Load Balancing Overview
- Round-Robin Algorithm
- Least Connections
- IP Hash
- Random Algorithm
- Weighted Load Balancing
- Least Time Algorithm
- Backup Servers
- Health Checks
- Keepalive Connections
- Advanced Configuration
- Monitoring and Troubleshooting
Load Balancing Overview
Load balancing distributes client requests across multiple backend servers to improve performance, reliability, and resource utilization. Nginx evaluates each request against the configured upstream servers using the selected algorithm.
Key considerations when choosing an algorithm:
- Session State: Stateful applications need consistent routing (IP hash, cookie)
- Server Capacity: Heterogeneous servers need weighted distribution
- Request Patterns: Vary by application (CPU vs I/O intensive)
- Health: Dead servers must be detected and removed
- Persistence: Some requests need affinity to specific servers
Round-Robin Algorithm
Round-robin is the default algorithm, distributing requests equally in sequence:
upstream backend {
server 192.168.1.100:8000;
server 192.168.1.101:8000;
server 192.168.1.102:8000;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Request distribution with three servers:
- Request 1 → Server 1
- Request 2 → Server 2
- Request 3 → Server 3
- Request 4 → Server 1
- And so on...
Round-robin suits homogeneous servers with identical capacity.
Least Connections
The least connections algorithm routes requests to the server currently handling the fewest active connections:
upstream backend {
least_conn;
server 192.168.1.100:8000;
server 192.168.1.101:8000;
server 192.168.1.102:8000;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
}
}
Least connections excels at:
- Applications with long-lived connections
- Variable request processing times
- Load balancing WebSocket connections
- Real-time applications with persistent connections
The algorithm improves server utilization by routing to less busy servers.
IP Hash
IP hash routes requests from the same client IP to the same upstream server:
upstream backend {
ip_hash;
server 192.168.1.100:8000;
server 192.168.1.101:8000;
server 192.168.1.102:8000;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
IP hash characteristics:
- Ensures session affinity without cookies
- Deterministic routing (same IP always routes to same server)
- Useful for stateful applications requiring session persistence
- Reduces cache misses when servers maintain local caches
With three servers, the hash function consistently routes requests:
- Client 192.168.100.1 → Server A
- Client 192.168.100.2 → Server B
- Client 192.168.100.3 → Server C
Random Algorithm
The random algorithm routes each request to a randomly selected upstream server:
upstream backend {
random;
server 192.168.1.100:8000;
server 192.168.1.101:8000;
server 192.168.1.102:8000;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
}
}
Use random with the least_conn option to select among two random servers:
upstream backend {
random two least_conn;
server 192.168.1.100:8000;
server 192.168.1.101:8000;
server 192.168.1.102:8000;
}
Random benefits:
- Simple load distribution
- Reduced hotspotting
- Suitable for stateless applications
random two least_connprovides near-optimal distribution with lower CPU overhead
Weighted Load Balancing
Assign weights to servers to reflect different capacities:
upstream backend {
server 192.168.1.100:8000 weight=3; # Receives 3x more requests
server 192.168.1.101:8000 weight=1; # Baseline weight
server 192.168.1.102:8000 weight=2; # Receives 2x more requests
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
}
}
With these weights, request distribution ratio is 3:1:2:
- Out of 6 requests: Server 1 gets 3, Server 2 gets 1, Server 3 gets 2
Weights work with any algorithm except ip_hash:
upstream backend {
least_conn;
server 192.168.1.100:8000 weight=4;
server 192.168.1.101:8000 weight=2;
}
Least Time Algorithm
Least time algorithm selects the server with minimum average response time:
upstream backend {
least_time header;
server 192.168.1.100:8000;
server 192.168.1.101:8000;
server 192.168.1.102:8000;
}
Two variants:
least_time header: Uses time to first byteleast_time last_byte: Uses time to complete response
Least time combines response time metrics with connection count to optimize overall throughput.
Backup Servers
Designate servers as backup, used only when primary servers are unavailable:
upstream backend {
server 192.168.1.100:8000;
server 192.168.1.101:8000;
server 192.168.1.102:8000 backup;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
}
}
Server 192.168.1.102 receives traffic only when Servers 1 and 2 are down.
Use multiple backup servers:
upstream backend {
server 192.168.1.100:8000 weight=5;
server 192.168.1.101:8000 weight=5;
server 192.168.1.102:8000 weight=2 backup;
server 192.168.1.103:8000 weight=2 backup;
}
Health Checks
Passive health checks detect failures when requests fail:
upstream backend {
server 192.168.1.100:8000 max_fails=3 fail_timeout=30s;
server 192.168.1.101:8000 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8000 max_fails=2 fail_timeout=60s;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
proxy_connect_timeout 2s;
proxy_read_timeout 5s;
}
}
Parameters:
max_fails: Number of failures before marking downfail_timeout: How long to wait before retryproxy_connect_timeout: Time allowed for connectionproxy_read_timeout: Time allowed for response
For active health checks, use the Nginx Plus health_checks module or third-party tools.
Keepalive Connections
Enable HTTP keepalive to upstream servers to reduce connection overhead:
upstream backend {
server 192.168.1.100:8000;
server 192.168.1.101:8000;
server 192.168.1.102:8000;
keepalive 32;
keepalive_requests 100;
keepalive_timeout 60s;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Critical settings:
keepalive: Number of idle connections to maintain per upstreamkeepalive_requests: Maximum requests per connection (reset after)keepalive_timeout: Idle timeout before closingproxy_http_version 1.1: Required for HTTP/1.1 keepaliveproxy_set_header Connection "": Removes Connection header, enabling keepalive
Advanced Configuration
Combine multiple upstream blocks for specialized scenarios:
upstream fast_api {
least_conn;
server 192.168.1.110:8000;
server 192.168.1.111:8000;
}
upstream slow_api {
server 192.168.1.120:8000;
keepalive 64;
}
upstream static_content {
server 192.168.1.130:80;
keepalive 128;
}
server {
listen 80;
server_name api.example.com;
location /api/v1/ {
proxy_pass http://fast_api;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location /api/slow/ {
proxy_pass http://slow_api;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_read_timeout 30s;
}
location /static/ {
proxy_pass http://static_content;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
Conditional routing based on request characteristics:
upstream api_servers {
least_conn;
server 192.168.1.100:8000;
server 192.168.1.101:8000;
}
upstream mobile_servers {
server 192.168.1.150:8000;
server 192.168.1.151:8000;
}
server {
listen 80;
server_name api.example.com;
set $backend "api_servers";
if ($http_user_agent ~* "mobile|android|iphone") {
set $backend "mobile_servers";
}
location / {
proxy_pass http://$backend;
}
}
Monitoring and Troubleshooting
Enable upstream statistics in Nginx:
server {
listen 8080;
location /nginx_status {
stub_status;
access_log off;
allow 127.0.0.1;
deny all;
}
}
Check status:
curl http://localhost:8080/nginx_status
Verify upstream configuration:
nginx -T | grep -A 20 "upstream backend"
Test upstream server connectivity:
nc -zv 192.168.1.100 8000
curl -v http://192.168.1.100:8000/health
Monitor connection counts:
netstat -an | grep ESTABLISHED | wc -l
netstat -an | grep TIME_WAIT | wc -l
Check Nginx error logs for upstream issues:
tail -f /var/log/nginx/error.log | grep upstream
Reload configuration without dropping connections:
sudo nginx -s reload
sudo systemctl reload nginx
Conclusion
Nginx provides flexible, powerful load balancing algorithms suited to diverse application requirements. Round-robin works for stateless services, least connections for persistent connections, IP hash for session affinity, and weighted distribution for heterogeneous infrastructure. Combining these algorithms with health checks, keepalive connections, and backup servers creates robust, resilient load balancing configurations that maintain high availability and optimal performance.


