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

  1. Load Balancing Overview
  2. Round-Robin Algorithm
  3. Least Connections
  4. IP Hash
  5. Random Algorithm
  6. Weighted Load Balancing
  7. Least Time Algorithm
  8. Backup Servers
  9. Health Checks
  10. Keepalive Connections
  11. Advanced Configuration
  12. 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_conn provides 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 byte
  • least_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 down
  • fail_timeout: How long to wait before retry
  • proxy_connect_timeout: Time allowed for connection
  • proxy_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 upstream
  • keepalive_requests: Maximum requests per connection (reset after)
  • keepalive_timeout: Idle timeout before closing
  • proxy_http_version 1.1: Required for HTTP/1.1 keepalive
  • proxy_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.