top of page

Building Smart Load Balancing Using HAProxy and Custom Health Endpoints

haproxy

In real-world production setups, checking whether a server is just alive is not enough.

A server may respond on port 80, but internally:


  • CPU could be overloaded

  • Memory could be exhausted

  • Requests could be timing out


Yet traditional health checks will still mark it as UP.

This is where HAProxy httpchk becomes extremely powerful.

In this blog, we’ll walk through a practical implementation where HAProxy:👉 Routes traffic based on actual system load, not just availability.


🧠 The Idea


Instead of asking:

“Is the server responding?”

We ask:

“Is the server healthy enough to serve traffic?”

⚙️ Step 1: Custom Health Endpoint Script


Below is the script deployed on both backend servers.


On builddevops1

[root@builddevops1 ~]# cat /usr/local/bin/health_endpoint.sh
#!/bin/bash
LOAD=$(cat /proc/loadavg | awk '{print $1}')
THRESHOLD=5
if (( $(echo "$LOAD <= $THRESHOLD" | bc -l) )); then
    echo "{\"status\":\"ok\",\"load\":$LOAD}" > /var/www/html/health.json
else
    echo "{\"status\":\"fail\",\"load\":$LOAD}" > /var/www/html/health.json
fi
[root@builddevops1 ~]#

Cron Job

[root@builddevops1 ~]# crontab -l* * * * * /usr/local/bin/health_endpoint.sh

Health Check Output

On builddevops2

[root@builddevops2 ~]# cat /usr/local/bin/health_endpoint.sh
#!/bin/bash
LOAD=$(cat /proc/loadavg | awk '{print $1}')
THRESHOLD=5
if (( $(echo "$LOAD <= $THRESHOLD" | bc -l) )); then
    echo "{\"status\":\"ok\",\"load\":$LOAD}" > /var/www/html/health.json
else
    echo "{\"status\":\"fail\",\"load\":$LOAD}" > /var/www/html/health.json
fi
[root@builddevops2 ~]#

Cron Job

[root@builddevops2 ~]#  crontab -l* * * * * /usr/local/bin/health_endpoint.sh

Health Check Output


🔍 What This Script Does


  • Reads system load from /proc/loadavg

  • Compares against threshold (5)

  • Generates JSON:

{"status":"ok","load":0.04}

or

{"status":"fail","load":5.60}

This becomes the decision input for HAProxy.


🌐 Step 2: HAProxy Configuration


frontend http_front
    bind *:80
    mode http
    default_backend nginx_backend
backend nginx_backend
    mode http
    option httpchk GET /health
    http-check expect status 200
    http-check expect string "\"status\":\"ok\""
    default-server inter 3s fall 2 rise 2
    server nginx1 172.31.31.115:80 check
    server nginx2 172.31.25.171:80 check

🧩 Understanding the Magic


🔹 option httpchk GET /health

HAProxy sends an HTTP request instead of just checking TCP.


🔹 http-check expect status 200

Ensures HTTP response is valid.


🔹 http-check expect string "\"status\":\"ok\""

This is the key logic:

  • HAProxy inspects response body

  • Looks for "status":"ok"

  • If not found → server marked DOWN


🔹 Health Check Timing

default-server inter 3s fall 2 rise 
  • Check every 3 seconds

  • 2 failures → DOWN

  • 2 successes → UP


🔄 Step 3: Load Balancing Behavior


Initial test:

[root@siddhesh ~]# for sid in {1..6}; do curl http://localhost;done
BUILDDEVOPS2.SIDDHESH.COM
BUILDDEVOPS1.SIDDHESH.COM
BUILDDEVOPS2.SIDDHESH.COM
BUILDDEVOPS1.SIDDHESH.COM
BUILDDEVOPS2.SIDDHESH.COM
BUILDDEVOPS1.SIDDHESH.COM
[root@siddhesh ~]#

👉 Traffic is distributed evenly (round-robin)


🔥 Step 4: Simulating High Load


Now we stress one server.

[root@builddevops1 ~]# stress-ng --cpu 4 --vm 2 --vm-bytes 1024M --timeout 120
sstress-ng: info:  [14094] setting to a 2 mins run per stressorstress-ng: info:  [14094] dispatching hogs: 4 cpu, 2 vm

📉 Health Check Failure


[root@siddhesh ~]# curl http://172.31.25.171/health
{"status":"fail","load":5.60}

👉 Load exceeded threshold → status becomes fail


🚦 Step 5: Automatic Traffic Shift


[root@siddhesh ~]# for sid in {1..6}; do curl http://localhost;done
BUILDDEVOPS2.SIDDHESH.COM
BUILDDEVOPS2.SIDDHESH.COM
BUILDDEVOPS2.SIDDHESH.COM
BUILDDEVOPS2.SIDDHESH.COM
BUILDDEVOPS2.SIDDHESH.COM
BUILDDEVOPS2.SIDDHESH.COM
[root@siddhesh ~]#

👉 HAProxy removes overloaded server from rotation

👉 All traffic goes to healthy node


🧠 What Just Happened?


Instead of simple health checks:

❌ TCP Alive = Healthy

✅ Load-Based Health = Intelligent Routing


🏗️ Architecture Flow


haproxy

⚡ Why This Matters in Production


✅ Benefits

  • Prevents overloaded nodes from receiving traffic

  • Improves response time

  • Avoids cascading failures

  • Fully automated failover

Comments

Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page