How to Configure Keepalived, VRRP Virtual IP Failover, and Health Checks on Ubuntu Server

High availability (HA) is a fundamental operational requirement for mission-critical infrastructure, such as ingress load balancers, database clusters, and internal API gateways. When a single node crashes or requires routine maintenance, client connections must be transparently preserved without requiring manual DNS records reconfiguration or extended downtime.

Keepalived solves this challenge on Linux by implementing the Virtual Router Redundancy Protocol (VRRP) (RFC 5798). By sharing a floating Virtual IP (VIP) across multiple Ubuntu nodes and evaluating service health via automated monitoring scripts, Keepalived delivers instantaneous sub-second failover.

Architectural Overview: VRRP State Machine and Floating Virtual IPs

Keepalived coordinates two or more clustered nodes to present a single, resilient IP endpoint to the network:

Component / Protocol Operational Role Implementation Details
VRRP Multicast Heartbeat & node state coordination Nodes exchange periodic multicast packets over 224.0.0.18 (IP protocol 112) at 1-second intervals.
MASTER Node Primary traffic handler Binds the Virtual IP (VIP) to its physical network interface and answers ARP requests. Configured with higher priority (e.g., 101).
BACKUP Node Passive standby handler Listens for master advertisement packets. Configured with lower priority (e.g., 100). Assumes VIP if master misses three consecutive intervals.
Track Scripts Dynamic health monitoring Executes local shell scripts to verify underlying daemons (e.g., Nginx or HAProxy). Reduces priority or forces FAULT state upon failure.

Prerequisites & Kernel sysctl Preparation

Before installing Keepalived, you must configure the Linux kernel to permit network services to bind to an IP address that is not yet locally assigned to an interface (non-local binding). This ensures load balancers like HAProxy or Nginx can start cleanly on standby nodes before a failover occurs.

Execute the following commands on both nodes:

# Enable non-local IP binding in sysctl
sudo bash -c 'cat <<EOF > /etc/sysctl.d/99-keepalived.conf
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
EOF'

# Reload sysctl parameters
sudo sysctl --system

Step-by-Step Configuration Guide

Step 1: Install Keepalived

Update the APT package repository and install Keepalived on both primary (Node 1: 192.168.1.11) and secondary (Node 2: 192.168.1.12) servers:

sudo apt update && sudo apt install -y keepalived

Step 2: Create the Service Health Check Script

Create a script on both servers to monitor the health of your upstream web server or reverse proxy (e.g., Nginx):

sudo bash -c 'cat <<"EOF" > /usr/local/bin/check_nginx.sh
#!/bin/bash
/usr/bin/killall -0 nginx
EOF'

sudo chmod +x /usr/local/bin/check_nginx.sh

Step 3: Configure Keepalived on the Primary Node (MASTER)

Create /etc/keepalived/keepalived.conf on Node 1 (replacing eth0 with your primary network interface name and adjusting subnet IPs):

vrrp_script check_service {
    script "/usr/local/bin/check_nginx.sh"
    interval 2
    weight -20
    fall 2
    rise 2
}

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

    authentication {
        auth_type PASS
        auth_pass Secr3tVrrpPass!
    }

    virtual_ipaddress {
        192.168.1.100/24 dev eth0 label eth0:vip
    }

    track_script {
        check_service
    }
}

Step 4: Configure Keepalived on the Secondary Node (BACKUP)

Create /etc/keepalived/keepalived.conf on Node 2 with matching virtual_router_id and authentication, but with lower priority:

vrrp_script check_service {
    script "/usr/local/bin/check_nginx.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 Secr3tVrrpPass!
    }

    virtual_ipaddress {
        192.168.1.100/24 dev eth0 label eth0:vip
    }

    track_script {
        check_service
    }
}

Step 5: Allow VRRP Multicast Traffic Through UFW

If Uncomplicated Firewall (UFW) is active, allow VRRP protocol traffic between both cluster nodes:

sudo ufw allow proto vrrp to 224.0.0.18
sudo ufw reload

Step 6: Start and Validate the Keepalived Service

Start and enable the daemon on both nodes:

sudo systemctl enable --now keepalived

Verify that the VIP (192.168.1.100) is active on Node 1:

ip addr show dev eth0

You should see 192.168.1.100/24 scope global secondary eth0:vip bound to the primary node, whilst the backup node maintains only its base IP.

Step 7: Perform Controlled Cutover Testing

  1. Stop Nginx on the master node: sudo systemctl stop nginx.
  2. The check_service track script detects the daemon outage, reducing Node 1’s effective priority to 81 (101 – 20).
  3. Node 2, with priority 100, immediately claims the VIP.
  4. Run ip addr show dev eth0 on Node 2 to verify that the VIP has migrated instantaneously without dropped TCP connections.
  5. Restart Nginx on Node 1 (sudo systemctl start nginx); Node 1 reclaims the VIP as priority returns to 101.

Troubleshooting Keepalived and VRRP Deployments

Consult this diagnostic matrix to resolve split-brain incidents and configuration errors:

Issue / Symptom Underlying Root Cause Remediation Procedure
Split-brain: Both nodes claim the Virtual IP simultaneously. Firewalls (UFW/iptables) or network switches are blocking VRRP multicast traffic (224.0.0.18 / IP protocol 112). Verify network rules allow protocol 112; run tcpdump -nn -i eth0 proto 112 to confirm bi-directional packet receipt.
Failover does not trigger when service crashes. The vrrp_script weight reduction is insufficient to bring the Master priority below the Backup priority. Calculate priority margins: ensure priority - weight < backup_priority (e.g., $101 – 20 = 81 < 100$).
Keepalived exits with permission denied on script execution. Keepalived runs scripts under unprivileged user permissions or missing execute bits. Run chmod +x /usr/local/bin/check_nginx.sh and specify script_user root inside the global Keepalived configuration if required.
Frequent flapping between Master and Backup states. High network jitter or transient CPU spikes causing Keepalived to miss heartbeat intervals. Increase advert_int to 2 seconds, or configure nopreempt on the master node to prevent automatic failback upon recovery.

Deploying Keepalived VRRP on Ubuntu Server establishes a resilient, production-grade failover foundation that guarantees continuous service availability across enterprise infrastructure.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.