When a single backend server can no longer handle your traffic, or you want redundancy, Nginx can distribute incoming requests across multiple backend servers or application instances — a technique called load balancing.
Prerequisites
- Nginx installed
- Two or more backend servers/instances running the same application
Step 1 — Define the Upstream Backend Group
sudo nano /etc/nginx/nginx.conf
http {
upstream backend_app {
server 10.0.0.10:3000;
server 10.0.0.11:3000;
server 10.0.0.12:3000;
}
}
Step 2 — Reference the Upstream in a Server Block
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Step 3 — Test and Reload
sudo nginx -t && sudo systemctl reload nginx
Load Balancing Methods
By default, Nginx uses round-robin (equal distribution). Other methods:
upstream backend_app {
least_conn;
server 10.0.0.10:3000;
server 10.0.0.11:3000;
}
least_conn sends new requests to whichever backend currently has the fewest active connections — better for requests with variable processing time.
Session Persistence (Sticky Sessions)
If your app stores session data locally per-server (not in a shared store like Redis), use IP-hash so a client always reaches the same backend:
upstream backend_app {
ip_hash;
server 10.0.0.10:3000;
server 10.0.0.11:3000;
}
Weighted Load Balancing
Send more traffic to a more powerful server:
upstream backend_app {
server 10.0.0.10:3000 weight=3;
server 10.0.0.11:3000 weight=1;
}
Marking a Server as Backup
upstream backend_app {
server 10.0.0.10:3000;
server 10.0.0.11:3000 backup;
}
The backup server only receives traffic if all primary servers are unavailable.
Handling a Failed Backend
upstream backend_app {
server 10.0.0.10:3000 max_fails=3 fail_timeout=30s;
server 10.0.0.11:3000 max_fails=3 fail_timeout=30s;
}
Nginx automatically stops sending requests to a server after 3 failures, retrying it after 30 seconds.
Common Errors
502 Bad Gateway with all upstreams marked down — verify backend servers are actually reachable from the Nginx host (check firewall rules between them, not just the public-facing firewall).
Uneven traffic distribution unexpectedly — confirm ip_hash isn't enabled if you expected even round-robin distribution; sticky sessions inherently produce uneven per-server load with a small client pool.
Best Practices
- Use a shared session store (Redis) instead of
ip_hashwhen possible, for more even load distribution - Set
max_fails/fail_timeoutso Nginx automatically routes around a down backend - Monitor backend health independently, not just through Nginx's basic failure detection
FAQ
Do I need a separate load balancer VPS, or can Nginx run on the same server as an app instance?
Either works; a dedicated Nginx load-balancer VPS is common for larger deployments, but running it alongside one of the app instances is fine for smaller setups.
Related Articles
- How to Install Nginx on Ubuntu & Debian
- Nginx as a Reverse Proxy for Node.js/Docker Apps
- Nginx Performance Tuning: Worker Processes, Caching & Gzip
