Nginx as Load Balancer
Configure Nginx to distribute traffic between multiple backend servers. Algorithms, health checks and SSL termination.
Nginx can distribute requests between multiple backends (round-robin, least connections, IP hash) for scalability and high availability.
Architecture
Client → Nginx (Load Balancer) → Backend 1 (10.0.0.1)
→ Backend 2 (10.0.0.2)
→ Backend 3 (10.0.0.3)Basic configuration (Round Robin)
nano /etc/nginx/conf.d/loadbalancer.confupstream backend_pool {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://backend_pool;
proxy_http_version 1.1;
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;
# Timeout
proxy_connect_timeout 10s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}nginx -t && systemctl reload nginxLoad balancing algorithms
Round Robin (default)
Distributes requests in cyclic sequence. Simple and fair if backends are equivalent.
upstream backend_pool {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}Least Connections
Sends request to the server with the fewest active connections. Ideal if requests have variable duration.
upstream backend_pool {
least_conn;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}IP Hash (sticky sessions)
Each IP is always sent to the same backend. Necessary for apps with server-side sessions.
upstream backend_pool {
ip_hash;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}Weights (servers with different capacity)
upstream backend_pool {
server 10.0.0.1:8080 weight=3; # receives 3x traffic
server 10.0.0.2:8080 weight=2;
server 10.0.0.3:8080 weight=1;
}Backup servers (failover)
upstream backend_pool {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080 backup; # used only if others are down
}Passive health check
Nginx automatically removes backends that don't respond:
upstream backend_pool {
server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
server 10.0.0.3:8080 max_fails=3 fail_timeout=30s;
}max_fails=3: after 3 consecutive errors the backend is marked downfail_timeout=30s: after 30s tries again
Active health check
Active health check (health_check directive) is only available in Nginx Plus (commercial version). On open source Nginx use passive health check or an external tool like Keepalived.
Load Balancer with SSL (SSL termination)
upstream backend_pool {
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# Backends receive unencrypted HTTP (faster)
location / {
proxy_pass http://backend_pool;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
}
}
server {
listen 80;
return 301 https://$host$request_uri;
}TCP/UDP Load Balancing (stream)
To balance non-HTTP traffic (MySQL, Redis, games):
stream {
upstream mysql_pool {
server 10.0.0.1:3306;
server 10.0.0.2:3306;
}
server {
listen 3306;
proxy_pass mysql_pool;
proxy_timeout 10s;
proxy_connect_timeout 5s;
}
}Stream block
The stream {} block must be at the main level of nginx.conf, NOT inside http {}.
Monitoring and debug
# Real-time status
tail -f /var/log/nginx/access.log
# See upstream where traffic comes from
log_format upstream '$remote_addr - [$time_local] "$request" '
'$status $upstream_addr $upstream_response_time';
# Statistics (if nginx-extras installed)
location /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}curl http://localhost/nginx_statusOutput:
Active connections: 12
server accepts handled requests
1234 1234 5678
Reading: 0 Writing: 1 Waiting: 11Load balancing test
# Verify which backend responds to requests
for i in {1..6}; do
curl -s http://yourdomain.com/api/whoami
echo
doneIf backends return their hostname, you'll see alternation between them.