Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run Scan×
Skip to content
VGSources
Blog

How to Set Up NGINX Load Balancing Across Multiple Servers

A practical NGINX guide covering upstream configuration, round robin, least connections, hashing, health checks, shared zones, sessions, TCP/UDP services, and rollout testing.
Length12 min Posted Quest giverVGSources Team
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

To load-balance several application servers with NGINX, define an upstream group in the http context, list each backend, and send a public location block’s proxy_pass directive to that group. Round robin is the default HTTP method. You can then choose least connections or a hash method, configure passive failure handling, and test the behavior before putting the proxy into production.

This guide shows a complete configuration, explains which method fits different workloads, distinguishes open-source passive checks from NGINX Plus active checks, and covers sessions, shared upstream state, TCP/UDP services, rollout checks, and common failure symptoms.

As an Amazon Associate I earn from qualifying purchases.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The basic NGINX load-balancer design

NGINX sits at the public address as a reverse proxy. Clients connect to NGINX; NGINX selects one server from an upstream group and forwards the request. The application servers can remain on private addresses and do not need to be exposed directly to the internet.

An upstream group is reusable. Every proxy location that references the group can use the same backend pool, while the balancing algorithm and server parameters stay in one place. The public-facing server block handles listening and routing; the upstream block handles membership and selection.

Minimal HTTP configuration

Place the upstream definition and the server block inside the http context of your NGINX configuration. Replace the hostnames and port with addresses reachable from the NGINX machine.

http {
    upstream app_backend {
        server app1.example.com:8080;
        server app2.example.com:8080;
        server app3.example.com:8080;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://app_backend;
            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;
        }
    }
}

The name app_backend is local to this configuration; it is not a DNS record. The value in proxy_pass must match that upstream name. The proxy headers shown are a conventional starting point: your application may require different trust rules or additional headers, so validate them against the framework and authentication layer you run.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What happens to a request

  1. The client resolves your public hostname and opens a connection to NGINX.
  2. NGINX matches the request to a server block and then a location.
  3. The upstream name in proxy_pass selects the backend group.
  4. The configured method chooses one available server, taking weights into account where the method supports them.
  5. NGINX forwards the request and returns the selected server’s response to the client.

Adding capacity and controlling traffic share

Add one server line for each backend. Every address must be reachable from the NGINX host, and every backend should serve the same application contract: routes, authentication behavior, response format, and health endpoint.

Weighted distribution

By default, NGINX distributes HTTP requests with round robin. A server’s weight changes its relative share rather than setting an absolute request count.

upstream app_backend {
    server app1.example.com:8080 weight=3;
    server app2.example.com:8080 weight=1;
    server app3.example.com:8080 weight=1;
}

In this example, app1 is offered roughly three times the traffic of either app2 or app3 when all servers are available and requests are comparable. Weight is useful when machines have different capacity, but it does not compensate for an application that has a slower dependency or a different failure rate.

Taking a server out of rotation

For a planned drain, remove the server from the upstream configuration and reload NGINX after validating the file. If you need to keep the line for a later return, use the server-state controls appropriate to your NGINX edition and deployment process. Always confirm how existing long-lived connections are handled before maintenance; a configuration reload does not turn an established application connection into a new request.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choosing the HTTP balancing method

There is no universally best algorithm. Choose according to request duration, backend capacity, cache locality, and whether the application has a shared state model.

Method Configuration Best fit Important limitation
Round robin Default; no method directive Similar servers and broadly comparable requests Successive requests from one client can reach different servers
Least connections least_conn; Requests have very different durations or connection counts At low load, selection is less accurate when workers do not share upstream state
IP hash ip_hash; Client-IP affinity for applications that cannot yet share sessions Many users behind one NAT can concentrate on one server; it is not shared state
Generic consistent hash hash $request_uri consistent; Cache-like workloads where a chosen key should remain local It hashes the key you choose, not a user identity or session automatically
Least time NGINX Plus HTTP method Latency-sensitive traffic where response time and active connections both matter Commercial NGINX Plus capability
Random NGINX Plus method Randomized selection, optionally combined with other selection logic Commercial NGINX Plus capability

Round robin for uniform application servers

Round robin is the default HTTP behavior. NGINX cycles through available servers while considering configured weights. It is a sensible first choice when servers have similar CPU and memory capacity and requests have similar cost.

Least connections for uneven request duration

Enable it inside the upstream block:

upstream app_backend {
    least_conn;
    server app1.example.com:8080;
    server app2.example.com:8080;
    server app3.example.com:8080;
}

NGINX favors the server with the fewest active connections, with weights still taken into account. This can reduce queueing when one request holds a connection much longer than another. It is not a latency monitor: a server can have few connections and still be slow because of a database or external service.

IP hash for client affinity

upstream app_backend {
    ip_hash;
    server app1.example.com:8080;
    server app2.example.com:8080;
    server app3.example.com:8080;
}

IP hashing derives the selection from the client IP and keeps that client on the same server while the selected server remains available. It can help legacy applications that store a session in local memory, but it is only an affinity mechanism. Mobile networks, corporate proxies, and carrier NAT can change or collapse client IPs, and a failed server still causes the client to move.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Generic hashing and cache locality

upstream cache_workers {
    hash $request_uri consistent;
    server cache1.example.com:8080;
    server cache2.example.com:8080;
    server cache3.example.com:8080;
}

Here the URI is the key, so identical paths tend to reach the same backend. The consistent option limits remapping when members are added or removed, which helps cache-like workloads. Do not use a URI hash as a substitute for user-session affinity unless the application’s state model actually matches the URI key.

Sessions, carts, and other application state

Round robin and least connections can send successive requests from one browser to different servers. If login sessions, shopping carts, upload state, or other data live only in a backend process, the user may appear logged out or lose data after a rebalance.

Prefer shared state where possible

A shared session store or another replicated state design lets any backend handle the next request. This is generally more resilient than relying on one client always returning to one machine, because a server can be replaced without taking every local session with it.

Use affinity as a deliberate compromise

IP hashing can keep a client on one backend while it is healthy. Document the trade-off: traffic may become uneven, NAT users may share one hash identity, and failover moves the client to another server. NGINX Plus also documents additional sticky-session persistence, but stickiness still does not replicate data between application servers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Test the actual state your application uses. A simple anonymous page can work while login cookies, WebSockets, carts, or multipart uploads fail when requests change backends.

Failure detection and removing unhealthy backends

Passive behavior in open-source NGINX and NGINX Plus

Both editions can react to failures observed during live transactions. When a backend repeatedly fails according to its configured failure parameters, NGINX can mark it unavailable temporarily and stop sending new requests to it. This is passive checking: a real client request must reach the server and produce a failure before NGINX learns that the server is unhealthy.

Choose failure parameters that match your request timeouts and traffic volume. A setting that is too aggressive can eject a server because of a brief dependency outage; a setting that is too lenient can send many users to a broken backend before it is removed. Watch error logs and upstream response metrics after changing them.

Active HTTP checks with NGINX Plus

NGINX Plus can send periodic health-check requests independently of user traffic and evaluate the response. This can detect a failed application before the next customer request arrives. The commercial health-check module requires the upstream group to reside in shared memory.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Expose a dedicated lightweight endpoint such as /healthz only when your application defines it. A useful endpoint checks the dependencies that matter for serving traffic, such as a database or queue, rather than merely proving that a process accepts TCP connections. Keep the endpoint fast and ensure it does not mutate data.

Recovery and re-entry

Health handling is not complete until you verify recovery. After repairing a backend, confirm that NGINX allows it back into rotation, then send requests that exercise the application rather than only checking that the port is open. If the server repeatedly leaves and re-enters rotation, investigate application logs, dependency latency, connection limits, and the health-check path instead of simply increasing thresholds.

Shared upstream zones and worker coordination

NGINX workers normally keep their own copy of upstream state. An upstream zone places the server-group configuration and related counters in shared memory so workers can coordinate.

http {
    upstream app_backend {
        zone app_backend 64k;
        least_conn;
        server app1.example.com:8080;
        server app2.example.com:8080;
        server app3.example.com:8080;
    }

    server {
        listen 80;
        location / {
            proxy_pass http://app_backend;
        }
    }
}

The size shown is an example allocation; choose a value appropriate for the number of members and features you use. A shared zone matters most when accurate cross-worker counters are important. The official guidance notes that least-connections selection can be less accurate at low load without a shared zone. Zones also enable dynamic upstream configuration scenarios supported by the edition and modules you run.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A zone does not make application data shared. It coordinates NGINX’s view of the upstream group; sessions, caches, and business records still need their own storage or replication strategy.

HTTP and HTTPS versus TCP and UDP services

HTTP and HTTPS

Use the http context and proxy_pass for HTTP traffic. TLS can terminate at NGINX or remain between NGINX and the backend according to your security design. If the application needs the original scheme, host, or client address, pass and validate the corresponding forwarding headers.

Raw TCP and UDP

For protocols that are not HTTP, use the stream context. The upstream syntax is similar, but routing is connection-oriented and there is no HTTP URI or header to proxy.

stream {
    upstream game_service {
        server game1.example.com:9000;
        server game2.example.com:9000;
    }

    server {
        listen 9000;
        proxy_pass game_service;
    }
}

NGINX documents weighted round robin as the default stream method and also supports least connections, least time, and hash selection for stream upstreams. Select a method based on connection duration and protocol behavior; an HTTP request-count assumption does not automatically apply to a long-lived TCP connection.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Safe deployment procedure

Use this sequence for a new pool or a change to an existing one. Perform it from the NGINX host or through your normal controlled deployment system.

  1. Confirm addresses and ports. From the NGINX host, verify that every backend address resolves as expected and that the configured port is reachable through local firewalls, security groups, and network routes.
  2. Check the application response. Request the real application path from each backend directly where policy permits. Verify status codes, required headers, authentication behavior, and the dedicated health endpoint if one exists.
  3. Define the upstream group. Add one server entry per backend. Choose round robin, least connections, or a hash method deliberately; add weights only when capacity differences justify them.
  4. Wire the public location. Set proxy_pass to the upstream name and configure forwarding headers that your application trusts. Check WebSocket or streaming requirements separately if the service uses long-lived connections.
  5. Validate syntax. Run nginx -t against the intended configuration. Do not reload if the test reports an error; fix the referenced file and line first.
  6. Reload without replacing the process. Use your operating system’s controlled NGINX reload mechanism after the syntax test succeeds. Keep the previous configuration available for a quick rollback.
  7. Generate repeated requests. Send enough requests to observe more than one backend. Inspect access logs and upstream variables or application logs to confirm the intended distribution.
  8. Simulate one failure. Stop or isolate one backend in a controlled window. Verify that passive behavior stops assigning new requests after the configured failures, or that an active check marks it unavailable when using NGINX Plus.
  9. Restore and observe recovery. Bring the backend back, confirm successful health behavior, and watch for repeated failure and recovery cycles.
  10. Exercise stateful paths. Test login, logout, carts, uploads, WebSockets, and any operation that depends on connection or session affinity. Anonymous requests alone are insufficient.
  11. Monitor after rollout. Track upstream response time, connection counts, failures, and 5xx responses. Compare backend logs so a proxy-level success rate does not hide one overloaded member.

Troubleshooting common symptoms

NGINX starts, but every request returns an upstream error

  • Check that the upstream name in proxy_pass exactly matches the declared group.
  • Verify the NGINX host can resolve each backend name and reach its port.
  • Inspect the error log for connection refusal, timeout, TLS, or name-resolution details.
  • Request the backend directly from the NGINX host to separate an application failure from a proxy-routing failure.

Only one server receives traffic

  • Confirm the other servers are not marked unavailable after passive failures.
  • Look for a weight that heavily favors one member.
  • Check whether a hash method or a single long-lived connection makes the traffic appear sticky.
  • Ensure your test sends multiple independent requests rather than reusing one connection and measuring only connection assignment.

Users lose login state after a few requests

  • Determine whether sessions are stored locally on each backend.
  • Move session data to a shared store or implement a supported replication strategy.
  • If a short-term affinity workaround is necessary, test ip_hash against NAT, mobile clients, and failover behavior.
  • Confirm that cookie, authorization, and forwarding headers survive the proxy path.

A backend is still receiving requests after it fails

  • Remember that passive checks require live transactions; no request means no new passive observation.
  • Review the failure parameters and proxy timeouts for the upstream.
  • For proactive detection, use an appropriate NGINX Plus active check and a meaningful health endpoint.
  • Check for existing long-lived connections, which may continue until the protocol or application closes them.

Least connections does not distribute as expected

  • Connection count is not the same as CPU use, request latency, or response size.
  • At low load, separate worker state can make selection less accurate.
  • Use a shared upstream zone when coordinated counters are important, then measure again under representative traffic.
  • Inspect whether one backend is handling long-lived streams that legitimately keep its active-connection count high.

Design choices at a glance

Decision Option A Option B How to decide
Protocol HTTP/HTTPS in http TCP/UDP in stream Use the context that matches the wire protocol; HTTP headers and URIs are unavailable in stream mode.
Balancing objective Even distribution Active-count, latency, or affinity selection Start with round robin for similar servers; change only when workload evidence supports it.
Failure detection Passive live-transaction failures Periodic active HTTP checks Passive behavior is available in both editions; active checks require NGINX Plus.
State model Shared session and data store Client affinity Shared state survives backend changes better; affinity is a compatibility measure with uneven-load risks.
Upstream state Worker-local copies Shared zone Use a zone when cross-worker counters or dynamic upstream management matter.
Change model Static file and reload Edition-supported dynamic management Use controlled syntax tests and reloads for static configuration; verify edition and module support before adopting dynamic changes.

Operational practices that prevent avoidable outages

  • Keep backend application versions compatible during rolling deployments. A load balancer cannot reconcile incompatible session formats or API responses.
  • Make health endpoints reflect the dependencies required for real traffic, but keep them inexpensive enough to check frequently.
  • Record which upstream member handled a request so an intermittent error can be traced to one backend.
  • Alert on rising upstream latency and 5xx responses, not only on whether the NGINX process is running.
  • Test one-member failure regularly in a maintenance window. A configuration that looks correct on paper may fail because of firewall rules, session handling, or an overlooked long-lived connection.
  • Keep the NGINX configuration under version control, and retain the last known-good file for rollback after every reload.

The reusable upstream group is the center of the design. Select the method according to workload and state, use passive or active health behavior with its edition limits understood, coordinate workers with a zone when needed, and verify failure, recovery, and session behavior with real requests before declaring the pool ready.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More quests from Patch Notes

  1. How to Set Up a RedM (RDR2) Server in 2026: License Key, txAdmin and server.cfg, Step by StepBlog11min
  2. Best RedM (RDR2) Server Hosting in 2026: Comparing Four Hosts on Slots, Memory and PriceBlog10min
  3. How to Host a Mindustry Server in 2026: server-release.jar, Port 6567 and the Commands That MatterBlog7min
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.