To load balance with nginx, list your app servers in an upstream block and point proxy_pass at its name. Requests rotate round robin unless you add least_conn, ip_hash, hash or random. Each server line can carry a weight, max_fails and fail_timeout for passive health checks, and backup or down flags.
| Directive | How it picks a server | Good for |
|---|---|---|
| (none) | Weighted round robin, the default | Similar servers and short requests |
| least_conn | Fewest active connections, weights taken into account | Requests of very different lengths |
| ip_hash | Hash of the client address (first three octets of IPv4, whole IPv6) | Keeping a visitor on one server |
| hash key | Hash of any value, such as $request_uri | Cache affinity; add consistent to limit reshuffling |
| random | Random server, or best of two with random two | Several load balancers sharing one pool |
Updated
Spreading traffic over several copies of an app is one of the oldest jobs nginx does, and the whole setup fits in a dozen lines. The builder below writes the upstream block from a list of servers, warns about combinations nginx rejects, and shows each server's share of requests when weights apply. The reference after it covers every server parameter with its default.
List your servers, choose a method and copy a ready-to-paste upstream and proxy block. The share column shows how weighted round robin divides requests.
Share applies to round robin and is approximate for least_conn; hash methods depend on the keys.
| Parameter | Default | Effect |
|---|---|---|
| weight=n | 1 | Relative share of requests |
| max_fails=n | 1 | Failed attempts within fail_timeout before the server is skipped; 0 disables |
| fail_timeout=time | 10s | Window for counting failures and how long the server is skipped |
| max_conns=n | 0 (no limit) | Cap on simultaneous active connections to this server |
| backup | off | Used only when all primary servers are unavailable |
| down | off | Marks the server permanently unavailable |
Two primaries share the load; the third only answers if both are out.
upstream app {
server 10.0.0.11:3000;
server 10.0.0.12:3000;
server 10.0.0.13:3000 backup;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}keepalive caches idle connections to the backends; the two proxy lines make that work.
upstream api {
least_conn;
server 10.0.0.21:8080 weight=2 max_fails=3 fail_timeout=30s;
server 10.0.0.22:8080 max_fails=3 fail_timeout=30s;
keepalive 16;
}
server {
listen 80;
location /api/ {
proxy_pass http://api;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_next_upstream error timeout http_502 http_503;
}
}Each URL keeps landing on the same cache server; consistent keeps most mappings intact when a server is added or removed.
upstream caches {
hash $request_uri consistent;
server 10.0.0.31:80;
server 10.0.0.32:80;
server 10.0.0.33:80;
}
The upstream block lives in the http context, next to your server blocks. It only names a pool; nothing happens until a location says proxy_pass http://name. The same pool can be used by many locations and many server blocks.
When a request to a server fails in a way that proxy_next_upstream counts, nginx retries it on the next server and notes the failure. Once a server reaches max_fails failures within the fail_timeout window, nginx stops choosing it. After fail_timeout passes, it gently sends real client requests again, and a success puts it back in rotation.
Because the checks ride on real traffic, a visitor may hit the first failed attempt. Short timeouts on proxy_connect_timeout keep that delay small.
No. The health_check directive belongs to the commercial NGINX Plus. Open source nginx relies on passive checks: if a request to a server fails max_fails times within fail_timeout, the server is skipped for fail_timeout and then tried again with live traffic.
max_fails defaults to 1 and fail_timeout to 10 seconds. One failed attempt therefore takes a server out for ten seconds. Setting max_fails=0 turns the accounting off for that server.
Use ip_hash to keep each client address on one server, or hash with a cookie variable such as $cookie_session for finer control. Cookie-based sticky directives exist only in the commercial edition.
It changes how many requests a server gets relative to the others. With weights 3, 1 and 1, the first server receives about three of every five requests under round robin. The default weight is 1.
Yes. Point proxy_pass at the upstream and forward the Upgrade and Connection headers. least_conn suits long-lived sockets better than round robin, because it counts how many connections each server already holds.
Related: Apache vs nginx, Caddy vs nginx, WebSocket in Node.js, CDN explained, HTTP status codes.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.