How to Configure Nginx Upstream Load Balancing with Passive Health Checks, Weighted Servers, and Least-connections Routing on Ubuntu Server

Learning how to configure Nginx upstream load balancing with passive health checks, weighted servers, and least-connections routing on Ubuntu Server is one of the most valuable skills for any system administrator managing high-traffic applications. When a single backend server can’t handle your traffic alone, Nginx steps in as a powerful reverse proxy and load balancer. It distributes incoming requests across multiple backend servers, keeping your application fast and available. This tutorial walks you through building a complete upstream load balancing setup from scratch. You’ll configure weighted server pools, enable passive health checks to remove failed backends automatically, and use the least-connections algorithm to route traffic intelligently. By the end, you’ll have a production-ready Nginx load balancer running on Ubuntu Server.

Prerequisites and Requirements for Nginx Upstream Load Balancing

Before you start, make sure you have everything in place. This tutorial targets intermediate Linux users. You should be comfortable using the terminal, editing files with a text editor, and managing services with systemctl.

Here’s what you need:

  • Ubuntu Server 22.04 LTS (or 20.04 LTS) , one load balancer node
  • Two or more backend servers (can be VMs or additional Ubuntu instances)
  • Nginx installed on the load balancer node
  • Root or sudo access on all servers
  • Basic knowledge of IP addressing and HTTP
  • Estimated time: 30–45 minutes

Your backend servers don’t need Nginx. They just need to serve HTTP traffic on port 80 or another port you choose. For testing purposes, you can run a simple Python HTTP server or install Apache on each backend node.

If Nginx isn’t installed yet, run this on your load balancer:

sudo apt update
sudo apt install nginx -y
sudo systemctl enable nginx
sudo systemctl start nginx

Check the installed version with nginx -v before continuing.

Step-by-Step Guide to Configure Weighted Servers and Least-Connections Routing

Related tutorial: How to Create and Configure Custom Systemd Services on Linux

Now you’ll build the upstream block and configure your load balancing strategy. Nginx uses the upstream directive inside the http block to define your backend server pool.

Step 1: Open the Nginx configuration file.

Create a new site configuration instead of editing the default file directly:

sudo nano /etc/nginx/sites-available/loadbalancer.conf

Step 2: Define your upstream server pool.

Paste the following configuration into the file. This sets up three backend servers with weights and the least-connections algorithm:

upstream backend_pool {
    least_conn;

    server 192.168.1.101:80 weight=3 max_fails=3 fail_timeout=30s;
    server 192.168.1.102:80 weight=2 max_fails=3 fail_timeout=30s;
    server 192.168.1.103:80 weight=1 max_fails=3 fail_timeout=30s;
}

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://backend_pool;
        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_connect_timeout 5s;
        proxy_read_timeout 30s;
    }
}

Step 3: Understand the key directives.

Here’s what each part does:

  • least_conn , Routes each new request to the backend with the fewest active connections. This is smarter than simple round-robin for uneven workloads.
  • weight=3 , Tells Nginx to send three times more traffic to this server compared to a server with weight=1. Use higher weights for more powerful machines.
  • max_fails=3 , This is your passive health check setting. After three consecutive failed requests, Nginx marks the server as unavailable.
  • fail_timeout=30s , The server stays marked as down for 30 seconds. After that, Nginx tries it again automatically.

Passive health checks don’t send separate probe requests. Nginx monitors real traffic and reacts when failures occur. This makes it lightweight and simple to set up. For active health checks, you’d need Nginx Plus or a third-party module.

Step 4: Enable the configuration.

Create a symlink to activate your new config:

sudo ln -s /etc/nginx/sites-available/loadbalancer.conf /etc/nginx/sites-enabled/

Remove the default site if it conflicts:

sudo rm /etc/nginx/sites-enabled/default

Step 5: Test and reload Nginx.

Always test your configuration before reloading. A syntax error will take down your load balancer:

sudo nginx -t

You should see:

nginx: configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

If the test passes, reload Nginx:

sudo systemctl reload nginx

Step 6: Verify traffic distribution.

Watch your Nginx access log in real time to confirm requests are spreading across backends:

sudo tail -f /var/log/nginx/access.log

You can also enable an upstream response header for debugging. Add this inside your location block temporarily:

add_header X-Upstream-Server $upstream_addr;

Then use curl -I http://your-domain.com several times and check which backend IP appears in the response header.

Step 7: Add a backup server (optional but recommended).

A backup server only receives traffic when all primary servers are down:

server 192.168.1.104:80 backup;

Add this line inside your upstream backend_pool block. It gives you a safety net during outages.

Troubleshooting Common Nginx Load Balancing Issues

Even with a clean setup, things can go wrong. Here are the most common problems and how to fix them.

Problem: Nginx returns 502 Bad Gateway.

This usually means all backend servers are unreachable or marked as failed. Check that your backends are running and accessible from the load balancer:

curl http://192.168.1.101:80

If that fails, check the backend server’s firewall rules. On Ubuntu, allow port 80:

sudo ufw allow 80/tcp

Also check your max_fails and fail_timeout values. If all servers hit their fail limit simultaneously, Nginx will return 502 until the timeout expires.

Problem: Traffic isn’t distributing as expected.

Confirm that least_conn is the first directive inside your upstream block. Nginx reads directives in order. If you accidentally placed it after the server lines, check your syntax carefully.

Problem: Configuration test fails with “unknown directive”.

The least_conn directive is part of the standard ngx_http_upstream_module, which ships with Nginx by default. If you see this error, your Nginx build may be missing the module. Reinstall Nginx from the official Ubuntu repositories to get the full build.

Problem: Sessions break between requests.

Least-connections routing doesn’t guarantee the same user hits the same server. If your application needs sticky sessions, add the ip_hash directive instead of least_conn. This routes each client IP to the same backend consistently.

Tip: Always keep your proxy_connect_timeout short. A value of 5 seconds means Nginx won’t wait long before marking a backend as failed and trying the next one.

Conclusion: Next Steps After Configuring Nginx Upstream Load Balancing

You’ve now completed the full process of how to configure Nginx upstream load balancing with passive health checks, weighted servers, and least-connections routing on Ubuntu Server. Your load balancer will now spread traffic intelligently, handle backend failures automatically, and prioritize your most capable servers using weights.

From here, consider adding SSL termination with Let’s Encrypt so all traffic between clients and Nginx is encrypted. You might also explore caching with proxy_cache to reduce backend load further. If your traffic grows significantly, look into horizontal scaling by adding more backend nodes to your upstream pool without any downtime , just reload Nginx after updating the config.

For deeper reading on upstream configuration options, the official Nginx load balancing admin guide is an excellent next resource.

Similar Posts