Load balancing with upstream

NGINX · Practical · 12 min

Running more than one application instance needs a balancer that also routes around failures.

An upstream block names a group of servers; proxy_pass http://name; sends requests to it.

Balancing methods:

  • round robin (default), optionally with weight=.
  • least_conn;: the server with the fewest active connections; good when request durations vary.
  • ip_hash; or hash $key consistent;: sticky mapping of clients or keys to servers.
  • random two least_conn;: pick two at random, take the less busy.

Passive health checks: with max_fails=3 fail_timeout=30s a server that fails 3 times within 30 seconds is marked unavailable for 30 seconds. What counts as a failure, and whether the request is retried on the next server, is set by proxy_next_upstream (by default error timeout). Retries are only done for non-idempotent methods like POST if you explicitly allow it. A server marked backup only receives traffic when the others are down. Active health checks (probing a URL) are an NGINX Plus feature.

Keepalive to upstreams avoids a new TCP connection per request: add keepalive 32; to the upstream, plus proxy_http_version 1.1; and proxy_set_header Connection ""; in the location. Without those two, NGINX uses HTTP/1.0 with Connection: close to the backends.

Example

upstream app {
    least_conn;
    server 10.0.0.11:8000 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:8000 max_fails=3 fail_timeout=30s;
    server 10.0.0.13:8000 backup;          # only when both are down
    keepalive 32;                          # idle connections kept per worker
}

server {
    listen 80;
    location / {
        proxy_pass http://app;
        proxy_http_version 1.1;            # needed for upstream keepalive
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_next_upstream error timeout http_502;
        proxy_next_upstream_tries 2;
    }
}

Remember

  • upstream groups servers; round robin by default, least_conn and hash available
  • max_fails/fail_timeout are passive health checks; backup is for failover
  • Upstream keepalive needs HTTP/1.1 and an empty Connection header

Watch out for

  • Adding keepalive in upstream but not proxy_http_version 1.1, so connections are still closed.

Check yourself

  1. Which method keeps sending the same client IP to the same backend?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: nginx.org/en/docs/http/load_balancing.html