Configure keepalived with HAProxy backend health monitoring for high availability load balancing

Intermediate 45 min Apr 30, 2026 64 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up keepalived with HAProxy to create a high availability load balancer cluster with automatic failover, backend health monitoring, and VRRP protocol for seamless traffic distribution across multiple servers.

Prerequisites

  • Two or more servers for load balancer cluster
  • Three or more backend servers
  • Root or sudo access
  • Basic networking knowledge

What this solves

High availability load balancing requires both load distribution and automatic failover when the primary load balancer fails. This tutorial shows you how to configure keepalived with HAProxy to create a resilient load balancer cluster that monitors backend server health and automatically switches between primary and backup load balancers using VRRP (Virtual Router Redundancy Protocol).

Step-by-step configuration

Update system packages

Start by updating your package manager on both load balancer servers to ensure you get the latest versions.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install HAProxy and keepalived

Install both HAProxy for load balancing and keepalived for VRRP failover on your primary and backup servers.

sudo apt install -y haproxy keepalived
sudo dnf install -y haproxy keepalived

Configure HAProxy with backend health checks

Create the HAProxy configuration with backend servers and health monitoring. This configuration will be identical on both load balancer nodes.

global
    daemon
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    log stdout local0

defaults
    mode http
    timeout connect 5000
    timeout client  50000
    timeout server  50000
    option httplog
    option dontlognull
    option redispatch
    retries 3
    maxconn 2000

frontend web_frontend
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/example.com.pem
    redirect scheme https if !{ ssl_fc }
    default_backend web_servers

backend web_servers
    balance roundrobin
    option httpchk GET /health
    http-check expect status 200
    server web1 203.0.113.10:80 check inter 10s fall 3 rise 2
    server web2 203.0.113.11:80 check inter 10s fall 3 rise 2
    server web3 203.0.113.12:80 check inter 10s fall 3 rise 2

listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 30s
    stats admin if TRUE

Create SSL certificate directory

Create the SSL certificate directory and add your certificate. For testing, you can use a self-signed certificate.

sudo mkdir -p /etc/ssl/certs
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
    -keyout /tmp/example.com.key \
    -out /tmp/example.com.crt \
    -subj "/C=US/ST=State/L=City/O=Organization/CN=example.com"
sudo cat /tmp/example.com.crt /tmp/example.com.key | sudo tee /etc/ssl/certs/example.com.pem
sudo chmod 600 /etc/ssl/certs/example.com.pem
sudo chown root:root /etc/ssl/certs/example.com.pem

Configure keepalived on primary server

Configure keepalived on your primary load balancer server. This server will have higher priority and own the virtual IP initially.

global_defs {
    notification_email {
        admin@example.com
    }
    notification_email_from lb1@example.com
    smtp_server localhost
    smtp_connect_timeout 30
    router_id LB_DEVEL
    script_user root
    enable_script_security
}

vrrp_script chk_haproxy {
    script "/usr/bin/curl -f http://localhost:8404/stats || exit 1"
    interval 2
    weight -2
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 110
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass mySecurePass123
    }
    virtual_ipaddress {
        203.0.113.100/24
    }
    track_script {
        chk_haproxy
    }
    notify_master "/etc/keepalived/master.sh"
    notify_backup "/etc/keepalived/backup.sh"
    notify_fault "/etc/keepalived/fault.sh"
}

Configure keepalived on backup server

Configure keepalived on your backup load balancer server with lower priority. Change the router ID and state to BACKUP.

global_defs {
    notification_email {
        admin@example.com
    }
    notification_email_from lb2@example.com
    smtp_server localhost
    smtp_connect_timeout 30
    router_id LB_BACKUP
    script_user root
    enable_script_security
}

vrrp_script chk_haproxy {
    script "/usr/bin/curl -f http://localhost:8404/stats || exit 1"
    interval 2
    weight -2
    fall 3
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass mySecurePass123
    }
    virtual_ipaddress {
        203.0.113.100/24
    }
    track_script {
        chk_haproxy
    }
    notify_master "/etc/keepalived/master.sh"
    notify_backup "/etc/keepalived/backup.sh"
    notify_fault "/etc/keepalived/fault.sh"
}

Create keepalived notification scripts

Create scripts that run when keepalived state changes. These scripts help with monitoring and logging state transitions.

#!/bin/bash
echo "$(date): Became MASTER" >> /var/log/keepalived-state.log
systemctl restart haproxy
#!/bin/bash
echo "$(date): Became BACKUP" >> /var/log/keepalived-state.log
#!/bin/bash
echo "$(date): Entered FAULT state" >> /var/log/keepalived-state.log
sudo chmod +x /etc/keepalived/*.sh
sudo touch /var/log/keepalived-state.log

Enable IP forwarding

Enable IP forwarding on both load balancer servers to allow proper routing of the virtual IP traffic.

echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf
echo 'net.ipv4.ip_nonlocal_bind = 1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Configure firewall rules

Configure firewall rules to allow HAProxy, keepalived VRRP traffic, and health check communications.

sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 8404/tcp
sudo ufw allow from 203.0.113.0/24 to any port 112
sudo ufw allow in on eth0 to 224.0.0.18
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --add-port=8404/tcp
sudo firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='203.0.113.0/24' port protocol='tcp' port='112' accept"
sudo firewall-cmd --permanent --add-rich-rule="rule family='ipv4' destination address='224.0.0.18' protocol value='vrrp' accept"
sudo firewall-cmd --reload

Start and enable services

Start HAProxy and keepalived services on both servers. Start HAProxy first, then keepalived.

sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl enable keepalived
sudo systemctl start keepalived

Create backend health check endpoint

Configure a simple health check endpoint on your backend servers. This example shows a basic health check for Nginx servers.

server {
    listen 80;
    server_name _;
    
    location /health {
        access_log off;
        return 200 "healthy\n";
        add_header Content-Type text/plain;
    }
    
    location / {
        return 200 "Backend server is running\n";
        add_header Content-Type text/plain;
    }
}
sudo ln -s /etc/nginx/sites-available/health /etc/nginx/sites-enabled/
sudo systemctl reload nginx

Verify your setup

Test your keepalived and HAProxy configuration with these verification commands.

# Check HAProxy status and backend health
sudo systemctl status haproxy
curl -s http://localhost:8404/stats | grep -A 10 web_servers

Check keepalived status and virtual IP

sudo systemctl status keepalived ip addr show | grep 203.0.113.100

Test load balancing through virtual IP

curl -H "Host: example.com" http://203.0.113.100

Check keepalived logs

sudo journalctl -u keepalived -f

Verify VRRP advertisements

sudo tcpdump -i eth0 proto 112

Test failover by stopping HAProxy on the master node and verifying the backup takes over:

# On master node - simulate failure
sudo systemctl stop haproxy

Check that backup became master

cat /var/log/keepalived-state.log

Verify VIP moved to backup

ip addr show | grep 203.0.113.100

Restart HAProxy and check failback

sudo systemctl start haproxy

Advanced health monitoring configuration

Configure advanced HAProxy health checks

Enhance backend monitoring with more sophisticated health checks that verify application functionality rather than just HTTP responses.

backend web_servers
    balance roundrobin
    option httpchk GET /api/health
    http-check expect status 200
    http-check expect string "status":"ok"
    server web1 203.0.113.10:80 check inter 5s fall 2 rise 3 slowstart 60s maxconn 100
    server web2 203.0.113.11:80 check inter 5s fall 2 rise 3 slowstart 60s maxconn 100
    server web3 203.0.113.12:80 check inter 5s fall 2 rise 3 backup

Add custom health check script

Create a more comprehensive health check script that monitors multiple aspects of the load balancer health.

#!/bin/bash

Check HAProxy process

if ! pgrep haproxy > /dev/null; then exit 1 fi

Check HAProxy stats endpoint

if ! curl -f -s http://localhost:8404/stats > /dev/null; then exit 1 fi

Check if any backend servers are available

STATS=$(curl -s http://localhost:8404/stats) AVAILABLE=$(echo "$STATS" | grep -c "web_servers.*UP") if [ "$AVAILABLE" -lt 1 ]; then exit 1 fi exit 0
sudo chmod +x /usr/local/bin/haproxy-health.sh

Update keepalived with enhanced monitoring

Replace the simple curl check with the comprehensive health script in your keepalived configuration.

vrrp_script chk_haproxy {
    script "/usr/local/bin/haproxy-health.sh"
    interval 3
    weight -5
    fall 2
    rise 2
    timeout 10
}

Monitoring and alerting integration

For production deployments, integrate your keepalived cluster with monitoring systems like Prometheus and Grafana for HAProxy monitoring to track failover events, backend health, and performance metrics.

Configure log aggregation

Set up centralized logging to track keepalived state changes and HAProxy backend health across your cluster.

$ModLoad imudp
$UDPServerRun 514
$UDPServerAddress 127.0.0.1

local0.*    /var/log/haproxy.log
& stop
sudo systemctl restart rsyslog

Consider implementing centralized logging with rsyslog to aggregate logs from all load balancer nodes for better operational visibility.

Common issues

SymptomCauseFix
Virtual IP doesn't appearVRRP authentication mismatchVerify auth_pass matches on both nodes
Both nodes become MASTERNetwork split-brain conditionCheck network connectivity and VRRP multicast traffic
HAProxy backend shows DOWNBackend health check failingVerify /health endpoint returns 200 on backend servers
Keepalived logs connection refusedHAProxy stats not accessibleCheck HAProxy stats socket permissions and binding
Failover not happeningHealth check script not executablesudo chmod +x /usr/local/bin/haproxy-health.sh
SSL certificate errorsCertificate file format or permissionsVerify PEM format and chmod 600 on certificate

Next steps

Running this in production?

Want this handled for you? Setting this up once is straightforward. Keeping it patched, monitored, backed up and tuned across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle private cloud infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.