Nginx as Load Balancer

Nginx can distribute requests between multiple backends (round-robin, least connections, IP hash) for scalability and high availability.

02

Architecture

Client → Nginx (Load Balancer) → Backend 1 (10.0.0.1) → Backend 2 (10.0.0.2) → Backend 3 (10.0.0.3)
03

Basic configuration (Round Robin)

bash
nano /etc/nginx/conf.d/loadbalancer.conf
nginx
upstream 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;
    }
}
bash
nginx -t && systemctl reload nginx
04

Load balancing algorithms

Round Robin (default)

Distributes requests in cyclic sequence. Simple and fair if backends are equivalent.

nginx
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.

nginx
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.

nginx
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)

nginx
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;
}
05

Backup servers (failover)

nginx
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
}
06

Passive health check

Nginx automatically removes backends that don't respond:

nginx
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;
}

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.

  • max_fails=3: after 3 consecutive errors the backend is marked down
  • fail_timeout=30s: after 30s tries again
07

Load Balancer with SSL (SSL termination)

nginx
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;
}
08

TCP/UDP Load Balancing (stream)

To balance non-HTTP traffic (MySQL, Redis, games):

nginx
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;
    }
}

The stream {} block must be at the main level of nginx.conf, NOT inside http {}.

09

Monitoring and debug

bash
# 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;
}
bash
curl http://localhost/nginx_status

Output:

Active connections: 12 server accepts handled requests 1234 1234 5678 Reading: 0 Writing: 1 Waiting: 11
10

Load balancing test

bash
# Verify which backend responds to requests
for i in {1..6}; do
  curl -s http://yourdomain.com/api/whoami
  echo
done

If backends return their hostname, you'll see alternation between them.

DeluxHost, opgericht in 2023, biedt hoogwaardige hostingoplossingen voor diverse digitale behoeften. Wij bieden gedeelde hosting, VPS en dedicated servers met geavanceerde beveiliging en wereldwijde datacenters.

© DeluxHost, Alle rechten voorbehouden. | BTW-nummer: IT17734661006
Alle systemen operationeel