Availability: Master Load Balancing with NGINX

March 31, 2026·4 min read
nginxload balancingavailability

The Need for Load Balancing

In a world where users demand sub-second latency and 100% uptime, the single-server model is a relic of the past. To achieve high performance, modern infrastructure relies on Horizontal Scaling—spinning up multiple server instances and distributing traffic among them. At the heart of this architecture is the Load Balancer. Whether you are using the open-source version or NGINX Plus, understanding the nuances of how traffic is routed can be the difference between a seamless user experience and a performance nightmare.

The foundation of NGINX load balancing is the upstream directive. This defines a pool of servers that NGINX will treat as a single destination. By defining an upstream, you decouple your entry point from your backend. These destinations can be a mix of domain names, IP addresses, or even Unix sockets.

upstream loadbalancer {
    server app1.example.com:8080;
    server app2.example.com:8080;
    server 172.31.58.179:8080;
}

Each upstream destination can be:

  • IP addresses
  • DNS records
  • Unix sockets
  • A mix of all of them

NGINX provides several methods for distributing load. Choosing the right one depends entirely on whether your application is Stateless or Stateful.

The Stateless Standard: Round Robin

Round Robin is the default algorithm. It passes requests to the next server in a rotating sequential manner.

upstream loadbalancer {
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}

server {
    listen 80;
    location / {
        proxy_pass http://loadbalancer;
    }
}

Each new client request is proxied to the next server in rotation, distributing traffic equally.

Advanced Round Robin with weights:

upstream loadbalancer {
    server app1.example.com weight=1;
    server app2.example.com weight=2;
    server app3.example.com backup;
}
  • weight=2: Server 2 gets twice as much traffic as server 1
  • backup: Server 3 only used when primary servers are unavailable

Least Connections

Routes requests to the server with the fewest active connections:

upstream loadbalancer {
    least_conn;
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}

Perfect for when requests have varying processing times.

The Resource-Aware: Least Connections

The most sophisticated algorithm - considers both connections and response time:

upstream loadbalancer {
    least_time header;
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}

Parameters:

  • header: Time to receive first byte
  • last_byte: Time to receive full response

Generic Hash

Use a custom key to determine routing:

upstream loadbalancer {
    hash $request_uri consistent;
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}

Great for:

  • Content caching scenarios
  • When you need control over request distribution
  • The consistent parameter minimizes redistribution when servers change

IP Hash

Uses client IP address for consistent routing:

upstream loadbalancer {
    ip_hash;
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}

Ideal for stateful applications where session persistence matters.

Random

Random server selection with optional load distribution:

upstream loadbalancer {
    random two least_time=last_byte;
    server app1.example.com;
    server app2.example.com;
    server app3.example.com;
}
  • random two: Pick 2 servers randomly
  • least_time=last_byte: Balance between them using least time algorithm

Health Checks

Upstream failures can happen for many reasons - server crashes, network issues, application errors. NGINX provides two types of health checks:

Active Health Checks (NGINX Plus)

Proactively sends requests to upstream servers at regular intervals to verify their status. This provides predictive data about potential failures before users are affected. Monitors real user request responses to detect failures. This shows what end-users actually experience. Always use both active and passive health checks together for comprehensive monitoring.

Choosing the Right Algorithm

AlgorithmBest ForStateful Support
Round RobinEqual traffic distributionNo
Least ConnectionsVariable request processing timesNo
Least TimePerformance-critical applicationsNo
Generic HashContent caching, custom routingYes
IP HashSession persistenceYes
RandomSimple randomizationNo

Real-World Example

Here's a complete configuration for a typical web application:

upstream web_servers {
    least_conn;
    server web1.example.com weight=3;
    server web2.example.com weight=2;
    server web3.example.com weight=1 backup;
}

server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://web_servers;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

NGINX load balancing is a powerful tool for building scalable, highly available applications. The key is choosing the right algorithm for your specific use case:

  • Stateless apps: Round Robin or Least Connections
  • Stateful apps: IP Hash or Generic Hash
  • Performance-critical: Least Time (NGINX Plus)
  • Caching scenarios: Generic Hash

Remember to implement proper health checks and monitoring to ensure your load balancer can handle failures gracefully. For detailed health check configuration and advanced features, check out the official NGINX documentation.


Reference: High Performance Load Balancing with NGINX