Back to Portfolio
Nginx AWS EC2 DevOps

Nginx Load Balancing Lab on AWS EC2

A hands-on lab guide to setting up Nginx as a load balancer across multiple EC2 instances — covering round-robin, least connections, IP hash, and health checks.

RJ
Romanch Jung Rayamajhi
April 2026
10 min read
Architecture Overview
Clients users / traffic
Nginx LB :80 load balancer
Backend 1 :8080 app server
Backend 2 :8080 app server
Backend 3 :8080 app server

Nginx distributes traffic across 3 backend EC2 instances inside the default VPC

Table of Contents
  1. Prerequisites & Lab Setup
  2. Launch EC2 Instances
  3. Set Up Backend Servers
  4. Install & Configure Nginx Load Balancer
  5. Load Balancing Algorithms
  6. Health Checks & Failover
  7. Testing the Load Balancer
  8. Monitoring & Logs
  9. Cleanup

STEP 01Prerequisites & Lab Setup

For this lab, we'll spin up 4 EC2 instances total:

You'll need:

ℹ️
We use t2.micro (free tier) for all instances. This lab costs almost nothing if you clean up after.

STEP 02Launch EC2 Instances

2.1 Security Groups

Create two security groups:

SG: load-balancer-sg

PortSourcePurpose
22Your IPSSH
800.0.0.0/0HTTP traffic

SG: backend-sg

PortSourcePurpose
22Your IPSSH
8080load-balancer-sgApp traffic from LB only

2.2 Launch Instances

Launch 4 instances — Ubuntu 22.04, t2.micro, all in the default VPC. Tag them:

Note down the private IPs of all 3 backend instances — you'll need them for the Nginx upstream config.

STEP 03Set Up Backend Servers

SSH into each backend instance and run a simple Python HTTP server that identifies itself:

terminal — on each backend (1, 2, 3)
# SSH into backend
ssh -i "your-key.pem" ubuntu@<backend-private-ip>

# Create a simple identifier page
echo "Hello from Backend 1" > index.html

# Start a simple HTTP server on port 8080
python3 -m http.server 8080 &

# Or use a persistent approach with nohup
nohup python3 -m http.server 8080 &
⚠️
Change the message to "Hello from Backend 2" and "Hello from Backend 3" on the other two servers. This is how you verify load balancing is working.

STEP 04Install & Configure Nginx Load Balancer

SSH into the nginx-lb instance:

terminal — nginx-lb instance
# Update and install Nginx
sudo apt update && sudo apt install -y nginx

# Verify
nginx -v

4.1 Configure Upstream & Load Balancing

/etc/nginx/sites-available/loadbalancer
upstream backend_pool {
    # Default: Round Robin
    server 10.0.1.101:8080;   # backend-1 private IP
    server 10.0.1.102:8080;   # backend-2 private IP
    server 10.0.1.103:8080;   # backend-3 private IP
}

server {
    listen 80;
    server_name _;

    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_set_header   X-Forwarded-Proto $scheme;

        # Timeouts
        proxy_connect_timeout  5s;
        proxy_read_timeout     10s;
    }
}
terminal — enable config
# Enable the site
sudo ln -s /etc/nginx/sites-available/loadbalancer /etc/nginx/sites-enabled/

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

# Test & reload
sudo nginx -t
sudo systemctl reload nginx

STEP 05Load Balancing Algorithms

Nginx supports multiple algorithms. Change the upstream block to experiment:

Round Robin (default)

Distributes requests evenly across all servers, one after another.

round robin
upstream backend_pool {
    server 10.0.1.101:8080;
    server 10.0.1.102:8080;
    server 10.0.1.103:8080;
}

Least Connections

Sends traffic to the server with the fewest active connections. Great for uneven workloads.

least connections
upstream backend_pool {
    least_conn;
    server 10.0.1.101:8080;
    server 10.0.1.102:8080;
    server 10.0.1.103:8080;
}

IP Hash (Sticky Sessions)

Same client always goes to the same backend. Useful when you need session persistence.

ip hash
upstream backend_pool {
    ip_hash;
    server 10.0.1.101:8080;
    server 10.0.1.102:8080;
    server 10.0.1.103:8080;
}

Weighted

Send more traffic to stronger servers.

weighted round robin
upstream backend_pool {
    server 10.0.1.101:8080 weight=5;  # gets 5x traffic
    server 10.0.1.102:8080 weight=3;
    server 10.0.1.103:8080 weight=1;
}
💡
After changing the upstream block, always run sudo nginx -t then sudo systemctl reload nginx.

STEP 06Health Checks & Failover

Nginx automatically performs passive health checks. If a backend fails, Nginx stops sending traffic to it:

health check config
upstream backend_pool {
    server 10.0.1.101:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.102:8080 max_fails=3 fail_timeout=30s;
    server 10.0.1.103:8080 max_fails=3 fail_timeout=30s;

    # Optional: backup server (only used when all others are down)
    # server 10.0.1.104:8080 backup;
}

What this does:

STEP 07Testing the Load Balancer

Hit the load balancer's public IP repeatedly and watch the responses rotate:

terminal — from your local machine
# Hit the LB 9 times and see round-robin in action
for i in {1..9}; do
  curl http://<nginx-lb-public-ip>
done

# Expected output:
Hello from Backend 1
Hello from Backend 2
Hello from Backend 3
Hello from Backend 1
Hello from Backend 2
Hello from Backend 3
Hello from Backend 1
Hello from Backend 2
Hello from Backend 3

Test Failover

Stop one backend and see Nginx automatically route around it:

terminal — on backend-2
# Kill the web server on backend-2
pkill -f "http.server"

# Now curl the LB again — backend-2 is skipped
Hello from Backend 1
Hello from Backend 3
Hello from Backend 1
Hello from Backend 3
🚀
It works! Nginx detected the failed backend and automatically routed traffic to healthy servers — zero downtime for users.

STEP 08Monitoring & Logs

terminal — useful commands
# Watch access logs in real-time
sudo tail -f /var/log/nginx/access.log

# Watch error logs (see failed backends)
sudo tail -f /var/log/nginx/error.log

# Check Nginx status
sudo systemctl status nginx

# Check active connections
# Add this location block to your config:
location /nginx_status {
    stub_status;
    allow 127.0.0.1;
    deny all;
}
# Then: curl http://localhost/nginx_status

STEP 09Cleanup

⚠️
Don't forget! Terminate all 4 EC2 instances when you're done to avoid charges. Go to EC2 Dashboard → select all instances → Instance state → Terminate.

Key Takeaways