Skip to content
Back to Blog
Linux

KeepAlived + HAProxy: High Availability Load Balancer

Build an active-passive HA load balancer cluster with Keepalived VRRP and HAProxy for zero-downtime failover in production.

Dec 2025
13 min read

Introduction

High availability for services requires both load balancing (HAProxy) and failover for the load balancer itself (Keepalived with VRRP). Together they create a fully redundant setup where a floating IP automatically moves to a standby server if the primary fails.

Architecture

TEXT
Clients → [Virtual IP: 10.0.0.100]
               |
    ┌──────────┴──────────┐
[HAProxy-1 (Master)]  [HAProxy-2 (Backup)]
  10.0.0.10              10.0.0.11
    |                      |
    └──────────┬───────────┘
               |
    ┌──────────┼──────────┐
[Web-1:80]  [Web-2:80]  [Web-3:80]

Installing HAProxy

BASH
apt install haproxy

Edit /etc/haproxy/haproxy.cfg:

TEXT
global
    log /dev/log local0
    log /dev/log local1 notice
    maxconn 50000
    user haproxy
    group haproxy
    daemon
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners

defaults
    log global
    mode http
    option httplog
    option dontlognull
    option forwardfor
    option http-server-close
    retries 3
    timeout connect 5s
    timeout client 30s
    timeout server 30s

# Statistics page
listen stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats auth admin:yourpassword

# Frontend: what HAProxy listens on
frontend web-frontend
    bind *:80
    bind *:443 ssl crt /etc/haproxy/certs/combined.pem
    
    # Redirect HTTP to HTTPS
    redirect scheme https if !{ ssl_fc }
    
    # Route by path
    acl is_api path_beg /api/
    use_backend api-servers if is_api
    default_backend web-servers

# Backend: the actual servers
backend web-servers
    balance roundrobin
    option httpchk GET /health HTTP/1.1
Host: localhost
    http-check expect status 200
    server web-1 10.0.1.10:80 check inter 2s rise 2 fall 3
    server web-2 10.0.1.11:80 check inter 2s rise 2 fall 3
    server web-3 10.0.1.12:80 check inter 2s rise 2 fall 3 backup

backend api-servers
    balance leastconn
    option httpchk GET /api/health
    server api-1 10.0.2.10:8080 check
    server api-2 10.0.2.11:8080 check

Installing Keepalived (VRRP Failover)

BASH
apt install keepalived

Master Configuration (10.0.0.10)

Create /etc/keepalived/keepalived.conf:

TEXT
vrrp_script check_haproxy {
    script "killall -0 haproxy"   # Check if haproxy is running
    interval 2
    weight -20                     # Reduce priority by 20 if check fails
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51           # Same on both nodes
    priority 100                   # Higher = preferred master
    advert_int 1                   # Send VRRP advertisements every 1 second
    authentication {
        auth_type PASS
        auth_pass YourSecretPass123
    }
    virtual_ipaddress {
        10.0.0.100/24 dev eth0    # The floating IP
    }
    track_script {
        check_haproxy
    }
    notify_master "/etc/keepalived/notify.sh MASTER"
    notify_backup "/etc/keepalived/notify.sh BACKUP"
    notify_fault "/etc/keepalived/notify.sh FAULT"
}

Backup Configuration (10.0.0.11)

Same file but change:

TEXT
state BACKUP
priority 90  # Lower than master

Notification Script

BASH
cat > /etc/keepalived/notify.sh << 'EOF'
#!/bin/bash
TYPE=$1
DATE=$(date +'%Y-%m-%d %H:%M:%S')
echo "$DATE - Keepalived state: $TYPE" >> /var/log/keepalived-state.log
echo "HAProxy node $(hostname) became $TYPE at $DATE" |   mail -s "HAProxy Failover: $TYPE" admin@company.com
EOF
chmod +x /etc/keepalived/notify.sh

Starting Services

BASH
# On both nodes
systemctl enable haproxy keepalived
systemctl start haproxy keepalived

# Check VIP assignment (only on master)
ip addr show eth0 | grep 10.0.0.100

# View keepalived logs
journalctl -u keepalived -f

Testing Failover

BASH
# Continuous test from client
while true; do curl -s http://10.0.0.100/ | grep "server-name"; sleep 1; done

# On master: stop haproxy
systemctl stop haproxy
# VIP should move to backup within 2-4 seconds

# Or disconnect network on master
ip link set eth0 down
# VIP moves to backup immediately

HAProxy Health Check Examples

TEXT
# HTTP health check
option httpchk GET /health
http-check expect status 200

# TCP health check (for databases)
option tcp-check

# Custom health check script
external-check command /usr/lib/nagios/plugins/check_mysql

# Health check with specific host header
option httpchk GET /health HTTP/1.1
Host: api.company.com

SSL Termination at HAProxy

BASH
# Combine certificate and key
cat /etc/ssl/certs/example.com.crt /etc/ssl/private/example.com.key > /etc/haproxy/certs/combined.pem
chmod 600 /etc/haproxy/certs/combined.pem

# Enable in HAProxy frontend
bind *:443 ssl crt /etc/haproxy/certs/combined.pem alpn h2,http/1.1
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets