Skip to main content

Load Balancing Configuration and Strategies

Now that you've mastered reverse proxy configuration, let's scale up! In this lesson, you'll learn how to distribute traffic across multiple backend servers using Nginx's powerful load balancing capabilities.

Learning Goals:

  • Configure basic load balancing with multiple backend servers
  • Understand and implement different load balancing algorithms
  • Configure health checks for backend servers
  • Handle session persistence when needed

Understanding Load Balancing

Load balancing distributes incoming network traffic across multiple backend servers to ensure no single server becomes overwhelmed. This provides:

  • High availability - if one server fails, others can handle traffic
  • Scalability - easily add more servers to handle increased load
  • Performance - distribute load to prevent bottlenecks

Basic Load Balancing Configuration

Let's start with a simple round-robin load balancer that distributes requests evenly across three backend servers.

load-balancer.conf
http {
upstream backend_servers {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
server 192.168.1.12:8080;
}

server {
listen 80;
server_name myapp.example.com;

location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}

In this configuration:

  • upstream defines a group of backend servers
  • Nginx automatically uses round-robin algorithm
  • Requests are distributed sequentially to each server
tip

Always use meaningful names for your upstream blocks. Names like backend_servers, api_cluster, or web_servers make your configuration more readable and maintainable.

Load Balancing Methods

Nginx supports several load balancing algorithms. Let's explore the most commonly used ones.

Round Robin (Default)

The default method where requests are distributed sequentially.

round-robin.conf
upstream backend {
server 10.0.1.10:80;
server 10.0.1.11:80;
server 10.0.1.12:80;
}

Least Connections

Directs traffic to the server with the fewest active connections.

least-conn.conf
upstream backend {
least_conn;
server 10.0.1.10:80;
server 10.0.1.11:80;
server 10.0.1.12:80;
}

IP Hash

Uses client IP address to determine which server receives the request, ensuring session persistence.

ip-hash.conf
upstream backend {
ip_hash;
server 10.0.1.10:80;
server 10.0.1.11:80;
server 10.0.1.12:80;
}

Weighted Distribution

Assigns weights to servers based on their capacity.

weighted.conf
upstream backend {
server 10.0.1.10:80 weight=3; # Handles 3x more traffic
server 10.0.1.11:80 weight=2;
server 10.0.1.12:80 weight=1;
}

Server Health Checks

Nginx can automatically detect and stop sending traffic to unhealthy servers.

health-checks.conf
upstream backend {
server 10.0.1.10:80 max_fails=3 fail_timeout=30s;
server 10.0.1.11:80 max_fails=3 fail_timeout=30s;
server 10.0.1.12:80 max_fails=3 fail_timeout=30s;
}
  • max_fails=3: Number of failed attempts before marking server unhealthy
  • fail_timeout=30s: How long to consider server unhealthy before retrying
warning

Don't set max_fails=1 for critical applications. Temporary network glitches can cause unnecessary server removal from the pool. Use at least 2-3 failures before marking a server down.

Advanced Load Balancing Features

Backup Servers

Designate backup servers that only receive traffic when primary servers are down.

backup.conf
upstream backend {
server 10.0.1.10:80;
server 10.0.1.11:80;
server 10.0.1.12:80 backup;
}

Session Persistence with Cookies

When IP hash isn't suitable (clients behind NAT), use cookies for session persistence.

sticky-cookie.conf
upstream backend {
sticky cookie srv_id expires=1h domain=.example.com path=/;
server 10.0.1.10:80;
server 10.0.1.11:80;
server 10.0.1.12:80;
}

Slow Start

Gradually increase traffic to newly added or recovered servers.

slow-start.conf
upstream backend {
server 10.0.1.10:80 slow_start=30s;
server 10.0.1.11:80 slow_start=30s;
server 10.0.1.12:80 slow_start=30s;
}

Complete Load Balancer Example

Here's a production-ready load balancer configuration combining multiple features:

production-lb.conf
http {
upstream app_cluster {
least_conn;

server 10.0.1.10:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.1.11:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.1.12:8080 weight=1 max_fails=3 fail_timeout=30s;
server 10.0.1.13:8080 backup;

keepalive 32;
}

server {
listen 80;
server_name app.company.com;

# Health check endpoint
location /nginx-health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}

location / {
proxy_pass http://app_cluster;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;

# Timeout settings
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
}
}
}

Common Pitfalls

  • Missing health checks: Without proper max_fails and fail_timeout, Nginx continues sending requests to dead servers
  • Session state issues: Using round-robin for stateful applications without sticky sessions
  • DNS caching: Nginx caches upstream DNS lookups - use IP addresses or implement DNS resolution properly
  • Uneven load distribution: Not considering server capacity differences when using round-robin
  • Ignoring keepalive connections: Not configuring keepalive in upstream blocks for HTTP/1.1 performance

Summary

You've learned how to configure Nginx as a robust load balancer using various algorithms like round-robin, least connections, and IP hash. You can now implement health checks, handle session persistence, and create production-ready load balancing configurations that ensure high availability and optimal performance for your applications.

Nginx Load Balancing and Upstream Configuration

Which load balancing method ensures that the same client always reaches the same backend server?

Question 1/4