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.
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:
upstreamdefines a group of backend servers- Nginx automatically uses round-robin algorithm
- Requests are distributed sequentially to each server
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.
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.
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.
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.
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.
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 unhealthyfail_timeout=30s: How long to consider server unhealthy before retrying
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.
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.
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.
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:
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_failsandfail_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
keepalivein 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?