> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Load Balancer

> Configuring Nginx as a reverse proxy load balancer demonstrating round-robin, weighted round-robin and ip_hash sticky sessions with Apache backends

Welcome back. In this lesson we configure an Nginx reverse proxy to act as a load balancer and demonstrate three common balancing methods: round-robin (default), weighted round-robin, and `ip_hash` (simple sticky sessions). The demo uses two Apache backend servers to show how Nginx can proxy to other web servers (Apache, LiteSpeed, etc.).

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/2df4tIL8w6_cZYgQ/images/Nginx-For-Beginners/Intermediate-Config/Demo-Load-Balancer/nginx-round-robin-apache-servers.jpg?fit=max&auto=format&n=2df4tIL8w6_cZYgQ&q=85&s=0748eb22e1349291b4fb52555f6acc63" alt="A diagram titled &#x22;Algorithms: Round Robin&#x22; showing an NGINX load balancer sending traffic via a round-robin router to two Apache web servers labeled 1 and 2. The illustration visualizes evenly distributing requests across the web servers." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Intermediate-Config/Demo-Load-Balancer/nginx-round-robin-apache-servers.jpg" />
</Frame>

Round-robin cycles requests evenly across the available backends (request 1 → backend A, request 2 → backend B, request 3 → backend A, and so on).

For weighted round-robin you assign weights to each backend so one receives proportionally more requests than the other.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/2df4tIL8w6_cZYgQ/images/Nginx-For-Beginners/Intermediate-Config/Demo-Load-Balancer/nginx-weighted-roundrobin-apache-10-1.jpg?fit=max&auto=format&n=2df4tIL8w6_cZYgQ&q=85&s=1cebcd4511b208e7d029eb8781a46b77" alt="A diagram showing an NGINX load balancer using a weighted round-robin algorithm to distribute traffic to two Apache web servers, with weights 10 and 1." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Intermediate-Config/Demo-Load-Balancer/nginx-weighted-roundrobin-apache-10-1.jpg" />
</Frame>

The `ip_hash` method pins clients to a backend based on a hash of the client IP — useful for simple session stickiness (for example, shopping carts) when no shared session store is available.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/2df4tIL8w6_cZYgQ/images/Nginx-For-Beginners/Intermediate-Config/Demo-Load-Balancer/nginx-ip-hash-load-balancer-diagram.jpg?fit=max&auto=format&n=2df4tIL8w6_cZYgQ&q=85&s=998e0bafea25d403fbc6b5acbd8da094" alt="A diagram showing an NGINX load balancer using IP-hash routing to distribute client requests across three web servers. Arrows indicate how the IP-hash maps clients to specific backend servers." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Intermediate-Config/Demo-Load-Balancer/nginx-ip-hash-load-balancer-diagram.jpg" />
</Frame>

## Environment overview

* One node serves as the Nginx load balancer (`nginx`).
* Two nodes run Apache and serve a simple HTML page (`node01` and `node02`).

Validate a backend’s site (example on `node01`):

```bash theme={null}
root@node01 ~ ➜  curl http://localhost
# (returns the page HTML; output shortened for clarity)
<!doctype html>
<html>
  <head>...</head>
  <body>
    <h1>Welcome</h1>
    <p>Served by node01</p>
  </body>
</html>
```

Check service status examples:

```bash theme={null}
# On a web backend:
root@node01 ~ ➜  systemctl status apache2

# On the load balancer (to verify nginx presence or absence):
root@nginx ~ ➜  systemctl status nginx
# If nginx is not installed you will see: "Unit nginx.service could not be found."
```

## Restrict direct access to backends (UFW)

Best practice: only allow the load balancer to reach backend HTTP ports. First, discover the load balancer IP (example from `ip a` on `nginx`):

```bash theme={null}
root@nginx ~ ➜  ip a
...
    inet 192.230.202.10/24 brd 192.230.202.255 scope global eth0
...
```

On each backend (`node01`, `node02`) allow HTTP access only from the load balancer IP:

```bash theme={null}
# On node01 and node02:
root@node01 ~ ➜ ufw allow from 192.230.202.10 proto tcp to any port 80
# Example response:
# Rule added
```

Confirm UFW rules:

```bash theme={null}
root@node01 ~ ➜ ufw status
Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere
80/tcp                     ALLOW       192.230.202.10
```

Useful references:

* UFW documentation: [https://help.ubuntu.com/community/UFW](https://help.ubuntu.com/community/UFW)
* Nginx docs (load balancing): [https://nginx.org/en/docs/http/load\_balancing.html](https://nginx.org/en/docs/http/load_balancing.html)

## Configure Nginx as a reverse proxy with upstreams

On the load balancer, edit the Nginx site config (example: `/etc/nginx/sites-available/apache-app`). Confirm the file exists:

```bash theme={null}
root@nginx /etc/nginx/sites-available ➜ ll
total 12
-rw-r--r-- 1 root root 443 Feb 10 20:53 apache-app
```

Start with an `upstream` block (the pool of backends) and a `server` block that proxies requests to it. Example default (round-robin) configuration:

```nginx theme={null}
# Upstream configuration
upstream apache_example {
    server 192.230.202.12:80;
    server 192.230.202.3:80;
}

# Default server configuration
server {
    listen 80;

    root /var/www/html;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    server_name apache.example.com;

    location / {
        proxy_pass http://apache_example;
    }
}
```

Notes:

* The example uses IP addresses for the backends (`192.230.202.12` for `node01` and `192.230.202.3` for `node02`). You may use DNS names instead.
* The `upstream` block above uses Nginx’s default round-robin algorithm.

Test configuration, enable the site and reload Nginx:

```bash theme={null}
root@nginx /etc/nginx/sites-available ➜ nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# Enable site (create symlink if needed)
root@nginx /etc/nginx/sites-available ➜ ln -s /etc/nginx/sites-available/apache-app /etc/nginx/sites-enabled/

# Reload nginx
root@nginx /etc/nginx/sites-available ➜ nginx -s reload
```

Test from the load balancer:

```bash theme={null}
root@nginx ~ ➜ curl http://localhost
# Returns the proxied HTML page. Refreshing the page shows alternating "Served by node01" / "Served by node02" indicating round-robin behavior.
```

## Load balancing methods — quick reference

|            Algorithm | Description                                            | When to use                                                                                       |
| -------------------: | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------- |
|          round-robin | Default. Cycles requests evenly across servers.        | Simple, stateless backends with similar capacity.                                                 |
| weighted round-robin | Assign weights to servers (`weight=`) to bias traffic. | Backends have different capacity or you want preferential routing.                                |
|            `ip_hash` | Maps client IP to a backend (simple sticky sessions).  | Simple session persistence without a shared session store; not ideal when many clients share IPs. |

## Weighted round-robin

To bias traffic toward one backend, add `weight=` to the server entries in the `upstream` block:

```nginx theme={null}
upstream apache_example {
    server 192.230.202.12:80 weight=10;
    server 192.230.202.3:80  weight=1;
}

server {
    listen 80;
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    server_name apache.example.com;

    location / {
        proxy_pass http://apache_example;
    }
}
```

* With the above weights, roughly 10 requests will go to `node01` for every 1 request to `node02`.
* Reload Nginx after changes:

```bash theme={null}
root@nginx /etc/nginx/sites-available ➜ nginx -s reload
```

Observe behavior in a browser or with repeated `curl` — the higher-weight backend should serve the majority of requests.

## ip\_hash (sticky sessions)

Enable basic session stickiness by adding `ip_hash` to the `upstream` block. This maps the client IP to a backend and keeps subsequent requests from that IP directed to the same server:

```nginx theme={null}
upstream apache_example {
    ip_hash;
    server 192.230.202.12:80;
    server 192.230.202.3:80;
}

server {
    listen 80;
    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    server_name apache.example.com;

    location / {
        proxy_pass http://apache_example;
    }
}
```

* Reload Nginx:

```bash theme={null}
root@nginx /etc/nginx/sites-available ➜ nginx -s reload
```

* After enabling `ip_hash`, the same client IP should consistently be mapped to the same backend. Refreshing from the same client should repeatedly show the same backend serving the request.

<Callout icon="lightbulb" color="#1CB2FE">
  ip\_hash provides simple sticky sessions by client IP. It is not suitable if clients are behind NAT/proxies that cause many clients to share an IP, and it does not account for backend health checks or capacity — consider more advanced session persistence strategies or a shared session store for production-grade stickiness.
</Callout>

## Quick reference — common files & commands

| Item                    | Purpose                             | Example                                                                 |
| ----------------------- | ----------------------------------- | ----------------------------------------------------------------------- |
| Nginx site file         | Configure upstreams and proxy rules | `/etc/nginx/sites-available/apache-app`                                 |
| Test config             | Syntax check before reload          | `nginx -t`                                                              |
| Enable site             | Activate site config                | `ln -s /etc/nginx/sites-available/apache-app /etc/nginx/sites-enabled/` |
| Reload Nginx            | Apply config changes                | `nginx -s reload`                                                       |
| Restrict backend access | Limit HTTP access to load balancer  | `ufw allow from 192.230.202.10 proto tcp to any port 80`                |

## Wrap-up

* Nginx upstreams default to round-robin load balancing.
* Use `weight=` to bias traffic (weighted round-robin).
* Use `ip_hash` to pin client IPs to backends for simple session persistence.
* Always validate changes with `nginx -t` and gracefully reload Nginx.
* Restrict backend access (for example with UFW) so only the load balancer can reach backend HTTP ports.

Further reading:

* Nginx load balancing docs: [https://nginx.org/en/docs/http/load\_balancing.html](https://nginx.org/en/docs/http/load_balancing.html)
* UFW documentation: [https://help.ubuntu.com/community/UFW](https://help.ubuntu.com/community/UFW)
* Apache HTTP Server: [https://httpd.apache.org/](https://httpd.apache.org/)

That completes this demo.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/c78ff9cb-c15d-4f85-92fc-abee5ed98b20/lesson/a2b8d77e-6682-4fa2-bedf-a8dde19a29ef" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/c78ff9cb-c15d-4f85-92fc-abee5ed98b20/lesson/da8e5437-fc04-466c-9446-8f856d5bb9ec" />
</CardGroup>
