A Linux routing daemon providing high availability via VRRP (Virtual Router Redundancy Protocol) and health checks for load-balanced server pools.

Table of Contents#

  1. Overview
  2. How VRRP Works
  3. Installation
  4. Basic Configuration
  5. VRRP Priorities and Preemption
  6. Health Check Scripts
  7. IPv6 and VRRPv3
  8. Notification Scripts
  9. Debugging and Diagnostics
  10. Troubleshooting
  11. See Also
  12. Sources

1. Overview#

Keepalived is a Linux-based routing daemon that provides high availability through VRRP and health checking. It enables multiple servers to share a virtual IP address (VIP), providing automatic failover when the active server becomes unreachable or a monitored service fails.

Key features:

  • VRRP (v2 and v3) - automatic VIP failover between servers
  • Health checks - monitor service availability (HTTP, TCP, scripts, process tracking)
  • LVS integration - direct integration with Linux Virtual Server for load balancing
  • IPv4 and IPv6 - dual-stack support via VRRPv3
  • Unicast and multicast - flexible VRRP advertisement transport

Common Use Cases#

Use CaseDescription
Database failoverVIP follows the primary PostgreSQL/MySQL node
Load balancer HAActive/standby HAProxy or Nginx pairs
Gateway redundancyDefault gateway failover for network segments
Service VIPAny service that needs a floating IP for client access

2. How VRRP Works#

VRRP defines a protocol where multiple routers (or servers) participate in a virtual router group. One is elected MASTER and owns the VIP; the others remain BACKUP.

Election Process#

  1. Each node has a priority (1-254; higher wins)
  2. The node with the highest effective priority becomes MASTER
  3. The MASTER sends periodic advertisements (default: every 1 second)
  4. If a BACKUP stops receiving advertisements for (3 * advert_int) + skew_time, it transitions to MASTER

State Transitions#

           +----------+
    +----->| BACKUP   |<-----+
    |      +----+-----+      |
    |           |             |
    |  (higher priority       | (MASTER seen with
    |   or timeout)           |  higher priority)
    |           v             |
    |      +----+-----+      |
    +------| MASTER   |------+
           +----------+

3. Installation#

Debian/Ubuntu#

sudo apt-get update
sudo apt-get install keepalived

RHEL/Rocky/Alma#

sudo dnf install keepalived

Arch Linux#

sudo pacman -S keepalived

Firewall Configuration#

VRRP uses IP protocol 112 (not a TCP/UDP port). Allow it through the firewall:

# firewalld
sudo firewall-cmd --add-rich-rule='rule protocol value="vrrp" accept' --permanent
sudo firewall-cmd --reload

# iptables
sudo iptables -A INPUT -p vrrp -j ACCEPT

# nftables
sudo nft add rule inet filter input ip protocol vrrp accept

For unicast mode, also allow the VRRP advertisement port (default UDP 112 between peers).


4. Basic Configuration#

The configuration file is /etc/keepalived/keepalived.conf.

MASTER Node#

global_defs {
    enable_script_security
    script_user root
    router_id <MASTER_HOSTNAME>
}

vrrp_track_process chk_service {
    process nginx
    weight 2
}

vrrp_instance VI_1 {
    interface <interface>
    virtual_router_id 27
    state MASTER
    priority 101
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass <secret>
    }

    virtual_ipaddress {
        <vip_address>/32
    }

    unicast_src_ip <master_ip>
    unicast_peer {
        <backup_ip>
    }

    track_process {
        chk_service
    }
}

BACKUP Node#

global_defs {
    enable_script_security
    script_user root
    router_id <BACKUP_HOSTNAME>
}

vrrp_track_process chk_service {
    process nginx
    weight 2
}

vrrp_instance VI_1 {
    interface <interface>
    virtual_router_id 27
    state BACKUP
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass <secret>
    }

    virtual_ipaddress {
        <vip_address>/32
    }

    unicast_src_ip <backup_ip>
    unicast_peer {
        <master_ip>
    }

    track_process {
        chk_service
    }
}

Service Management#

sudo systemctl enable --now keepalived
sudo systemctl status keepalived

5. VRRP Priorities and Preemption#

Priority Mechanics#

PriorityRole
255Reserved (IP address owner)
101-254Typical MASTER range
1-100Typical BACKUP range
0Reserved (signals MASTER is stopping)

The effective priority is the configured priority adjusted by weight values from tracked scripts/processes/interfaces:

effective_priority = base_priority + weight (if check passes)
effective_priority = base_priority - abs(weight) (if check fails and weight < 0)

Preemption Modes#

ModeBehavior
Preempt (default)A recovered higher-priority node reclaims MASTER
NopreemptOnce a BACKUP becomes MASTER, it stays MASTER until it fails
Preempt delayHigher-priority node waits N seconds before preempting
vrrp_instance VI_1 {
    state BACKUP
    priority 101
    nopreempt            # Do not preempt; stay BACKUP if another node is MASTER

    # Or use a delay before preempting:
    # preempt_delay 300  # Wait 5 minutes before taking over
}

When using nopreempt, both nodes should be configured with state BACKUP so the first to start becomes MASTER, and a recovered node does not immediately take over.

Multiple VRRP Instances#

For active/active setups, run two VRRP instances with opposite priorities:

# Node A
vrrp_instance VI_WEB {
    priority 101
    virtual_ipaddress { 192.0.2.100/32 }
}
vrrp_instance VI_DB {
    priority 100
    virtual_ipaddress { 192.0.2.101/32 }
}

# Node B
vrrp_instance VI_WEB {
    priority 100
    virtual_ipaddress { 192.0.2.100/32 }
}
vrrp_instance VI_DB {
    priority 101
    virtual_ipaddress { 192.0.2.101/32 }
}

6. Health Check Scripts#

vrrp_track_process#

Track a running process by name:

vrrp_track_process chk_nginx {
    process nginx
    weight 2           # Add 2 to priority when nginx is running
    quorum 1           # Minimum number of matching processes
    delay 2            # Wait 2 seconds before reacting to changes
}

vrrp_script#

Execute a custom script for health checking:

vrrp_script chk_haproxy {
    script "/usr/local/bin/check_haproxy.sh"
    interval 2         # Check every 2 seconds
    weight -20         # Subtract 20 from priority on failure
    fall 3             # Require 3 consecutive failures to mark as failed
    rise 2             # Require 2 consecutive successes to mark as recovered
    timeout 5          # Script timeout in seconds
}

vrrp_instance VI_1 {
    track_script {
        chk_haproxy
    }
}

Example Health Check Scripts#

HAProxy Check#

#!/usr/bin/env bash
# /usr/local/bin/check_haproxy.sh
if pgrep -x haproxy > /dev/null; then
    exit 0
fi
exit 1

HTTP Endpoint Check#

#!/usr/bin/env bash
# /usr/local/bin/check_http.sh
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/health)
if [ "${HTTP_CODE}" = "200" ]; then
    exit 0
fi
exit 1

PostgreSQL Primary Check#

#!/usr/bin/env bash
# /usr/local/bin/check_pgsql_primary.sh
RESULT=$(su - postgres -c "psql -tAc 'SELECT pg_is_in_recovery();'" 2>/dev/null)
if [ "${RESULT}" = "f" ]; then
    exit 0  # Is primary
fi
exit 1  # Is standby or unreachable

TCP Port Check#

#!/usr/bin/env bash
# /usr/local/bin/check_port.sh
if /usr/bin/nc -z localhost 5432 2>/dev/null; then
    exit 0
fi
exit 1

Track Interface#

Monitor a network interface (failover if the interface goes down):

vrrp_instance VI_1 {
    track_interface {
        eth0 weight -50
        eth1 weight -50
    }
}

7. IPv6 and VRRPv3#

VRRPv3 (RFC 5798) supports both IPv4 and IPv6 and removes the authentication mechanism (authentication is handled at the network layer instead).

IPv6 VIP Configuration#

vrrp_instance VI_6 {
    interface eth0
    virtual_router_id 61
    state MASTER
    priority 101
    advert_int 1
    use_vmac                   # Recommended for IPv6
    vmac_xmit_base             # Transmit on the base interface

    virtual_ipaddress {
        2001:db8::1/64
    }

    # No authentication block (VRRPv3 does not support it)
}

Dual-Stack Configuration#

Run separate VRRP instances for IPv4 and IPv6:

vrrp_instance VI_4 {
    interface eth0
    virtual_router_id 51
    state MASTER
    priority 101
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass <secret>
    }

    virtual_ipaddress {
        192.0.2.100/24
    }
}

vrrp_instance VI_6 {
    interface eth0
    virtual_router_id 61
    state MASTER
    priority 101
    advert_int 1
    use_vmac
    vmac_xmit_base

    virtual_ipaddress {
        2001:db8::100/64
    }
}

Forcing VRRPv3#

global_defs {
    vrrp_version 3
}

8. Notification Scripts#

Keepalived can execute scripts on state transitions:

vrrp_instance VI_1 {
    notify_master "/usr/local/bin/keepalived-notify.sh MASTER"
    notify_backup "/usr/local/bin/keepalived-notify.sh BACKUP"
    notify_fault  "/usr/local/bin/keepalived-notify.sh FAULT"
    notify_stop   "/usr/local/bin/keepalived-notify.sh STOP"

    # Or use a single notify script that receives state as argument:
    # notify "/usr/local/bin/keepalived-notify.sh"
}

Example notification script:

#!/usr/bin/env bash
# /usr/local/bin/keepalived-notify.sh
STATE=$1
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

echo "${TIMESTAMP} - Keepalived transitioned to ${STATE}" >> /var/log/keepalived-notify.log

case "${STATE}" in
    MASTER)
        # Actions when becoming MASTER
        systemctl start haproxy 2>/dev/null
        ;;
    BACKUP|FAULT|STOP)
        # Actions when losing MASTER
        systemctl stop haproxy 2>/dev/null
        ;;
esac

9. Debugging and Diagnostics#

Log Inspection#

# Keepalived logs to syslog by default
journalctl -u keepalived -f

# Or in syslog
tail -f /var/log/syslog | grep -i keepalived

Dump VRRP State#

# Send SIGUSR1 to dump current data to /tmp/keepalived.data
sudo kill -USR1 $(cat /run/keepalived.pid)
cat /tmp/keepalived.data

Dump Statistics#

# Send SIGUSR2 to dump stats to /tmp/keepalived.stats
sudo kill -USR2 $(cat /run/keepalived.pid)
cat /tmp/keepalived.stats

Verify VIP Assignment#

# Check if VIP is assigned
ip addr show dev <interface> | grep <vip_address>

# Watch VRRP advertisements (multicast mode)
sudo tcpdump -i <interface> -nn proto 112

# Watch VRRP advertisements (unicast mode)
sudo tcpdump -i <interface> -nn host <peer_ip> and proto 112

Configuration Validation#

# Check config syntax
keepalived --config-test -f /etc/keepalived/keepalived.conf

Verbose Logging#

global_defs {
    vrrp_garp_master_delay 1
    vrrp_garp_master_repeat 3
    log_unknown_vrids
}

Or start keepalived with increased verbosity:

keepalived -D -l -f /etc/keepalived/keepalived.conf

Troubleshooting#

IssueCauseSolution
VIP not assigned to any nodeVRRP traffic blocked by firewallAllow protocol 112 (VRRP) in firewall; for unicast, also allow peer IPs
Both nodes claim MASTER (split brain)Network partition or virtual_router_id mismatchEnsure both nodes share the same virtual_router_id; verify network connectivity between peers
VIP flapping between nodesHealth check script flapping or too aggressive timingsIncrease fall/rise counts in vrrp_script; check script reliability
BACKUP never takes overFirewall blocking, wrong priorities, or nopreempt misconfiguredVerify BACKUP receives advertisements; check priorities; test by stopping keepalived on MASTER
Gratuitous ARP not reaching clientsSwitches filtering GARP or GARP count too lowIncrease vrrp_garp_master_repeat; check switch ARP table
Health check script not runningenable_script_security set but script not owned by rootEnsure script is owned by root and not writable by group/other; or adjust script_user
VIP assigned but clients cannot connectService not running on the new MASTERAdd notify_master script to start the service; or use track_script/track_process
"Received lower priority advert" in logsNormal VRRP behavior when a lower-priority node startsInformational only; no action needed unless it leads to flapping

See Also#

Sources#