HAProxy active-passive failover with Keepalived isn’t about having two HAProxy instances ready to take over; it’s about one instance being the sole active traffic director at any given moment, with the other sitting silently, waiting for the first to die.
Let’s see this in action. Imagine two identical servers, haproxy1 and haproxy2, both running HAProxy and Keepalived.
On haproxy1 (the primary):
# /etc/keepalived/keepalived.conf
vrrp_script haproxy_check {
script "/usr/local/bin/check_haproxy.sh"
interval 2
weight 20
fall 2
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 mysecretpassword
}
virtual_ipaddress {
192.168.1.100/24 dev eth0
}
track_script {
haproxy_check
}
}
On haproxy2 (the secondary):
# /etc/keepalived/keepalived.conf
vrrp_script haproxy_check {
script "/usr/local/bin/check_haproxy.sh"
interval 2
weight 20
fall 2
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass mysecretpassword
}
virtual_ipaddress {
192.168.1.100/24 dev eth0
}
track_script {
haproxy_check
}
}
And the check_haproxy.sh script on both servers:
#!/bin/bash
if pgrep -x haproxy > /dev/null
then
exit 0
else
exit 1
fi
The magic here is Keepalived’s VRRP (Virtual Router Redundancy Protocol). It allows a group of servers to share a virtual IP address. The server with the highest priority (100 on haproxy1) becomes the MASTER and holds the virtual IP 192.168.1.100. The other server (haproxy2) is in BACKUP state with a lower priority (90) and waits. Both servers constantly send VRRP advertisements. If the MASTER stops sending advertisements, the BACKUP server detects this failure and transitions to MASTER, taking over the virtual IP. The vrrp_script adds another layer: if the HAProxy process itself crashes on the MASTER, the script returns a non-zero exit code, which decrements the priority. If the priority drops low enough, the MASTER can voluntarily relinquish the virtual IP, allowing the BACKUP to take over before it even misses any VRRP advertisements.
The core problem this solves is single points of failure. Without this, if your single HAProxy server goes down, your entire application becomes unreachable. By having a standby, you ensure that traffic can continue to flow even if the primary HAProxy instance fails. The virtual_ipaddress is the key abstraction; clients connect to 192.168.1.100, unaware of which physical server is currently serving their requests.
The track_script is crucial for a swift failover. Without it, Keepalived only reacts to the absence of VRRP advertisements. This means the backup might wait several seconds (based on advert_int and detection timeouts) before realizing the master is down. If HAProxy crashes but the OS is still running, the script will detect the missing process and signal Keepalived to lower the priority, triggering a failover much faster, often within a second or two. This minimizes downtime for your users.
Many people set state to MASTER on the primary and BACKUP on the secondary. While this works, it’s generally better practice to set both to BACKUP initially. Keepalived will then elect the MASTER based on priority. This makes it easier to manage during initial setup or if you need to manually move the VIP. The virtual_router_id must be unique for each VRRP group on the network segment.
The authentication section is vital for security. It ensures that only legitimate nodes in the VRRP group can claim the virtual IP. Without it, a rogue machine on your network could potentially spoof VRRP advertisements and hijack your virtual IP. auth_type PASS and auth_pass are the simplest forms, but AH (IP Authentication Header) is more secure if your network environment supports it.
The interface must be the network interface where the virtual IP will be bound and through which VRRP advertisements will be sent. Ensure this interface is up and configured on both machines.
The actual HAProxy configuration on both machines (/etc/haproxy/haproxy.conf) should be identical. This includes the backend server definitions, ACLs, and listen directives. When the virtual IP moves, the new MASTER HAProxy instance will immediately start serving traffic based on this identical configuration.
The most surprising part for many is how HAProxy itself doesn’t need to be "aware" of Keepalived. Keepalived manages the IP address at the OS level. HAProxy simply binds to whatever IP addresses are available on the server. When the virtual IP is assigned to a server, HAProxy can bind to it. When it’s unassigned, HAProxy can no longer serve traffic on it. The check_haproxy.sh script is the bridge, allowing Keepalived to react to HAProxy’s operational status.
The next thing you’ll likely want to tackle is active-active load balancing, where both HAProxy instances actively serve traffic simultaneously.