Keepalived implements the Virtual Router Redundancy Protocol (VRRP) on Linux, allowing two or more servers to share a single virtual IP address (VIP). If the master server fails, the backup server automatically claims the VIP within seconds, providing high availability with no changes required on clients or DNS. On RHEL 9, Keepalived integrates cleanly with systemd and firewalld, making it the standard approach for HA pairs without requiring a dedicated load balancer. This tutorial configures a master and backup server with health-check tracking so the VIP automatically moves when Nginx stops responding.

Prerequisites

  • Two RHEL 9 servers on the same Layer 2 network segment (master and backup)
  • Root or sudo access on both servers
  • Nginx (or another web service) installed on both servers
  • A free IP address on the same subnet to use as the virtual IP (e.g., 192.168.1.200)
  • Both servers able to communicate with each other; VRRP (protocol 112) allowed by firewall

Step 1 — Installing Keepalived on Both Servers

Install Keepalived from the standard RHEL 9 AppStream repository on both the master and the backup server. Also ensure Nginx is installed and running, as the health check script will monitor it.

# Run on BOTH master and backup servers
sudo dnf install -y keepalived nginx

sudo systemctl enable --now nginx
sudo systemctl status nginx

# Allow VRRP traffic through firewalld on both servers
sudo firewall-cmd --add-rich-rule='rule protocol value="112" accept' --permanent
sudo firewall-cmd --reload

Step 2 — Creating the Health Check Script

Keepalived can run a script at regular intervals and lower the VRRP priority of the local node if the script exits non-zero. This causes the backup server (which has a higher effective priority) to take over the VIP. Create the script on both servers.

sudo tee /etc/keepalived/check_nginx.sh <<'EOF'
#!/bin/bash
# Exit 0 if nginx is running, non-zero otherwise
if systemctl is-active --quiet nginx; then
    exit 0
else
    exit 1
fi
EOF

sudo chmod +x /etc/keepalived/check_nginx.sh

Step 3 — Configuring Keepalived on the Master Server

Create the Keepalived configuration on the master server. Replace ens3 with your actual interface name (check with ip link). The virtual_router_id must be identical on both master and backup and unique on the network segment. The authentication password must also match on both nodes.

sudo tee /etc/keepalived/keepalived.conf <<'EOF'
global_defs {
    router_id MASTER_NODE
    script_user root
    enable_script_security
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
    interval 2        # Run every 2 seconds
    weight  -30       # Lower priority by 30 if script fails
    fall    2         # Require 2 consecutive failures before acting
    rise    2         # Require 2 consecutive successes to recover
}

vrrp_instance VI_1 {
    state MASTER
    interface ens3
    virtual_router_id 51
    priority 100
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass S3cur3Pass!
    }

    virtual_ipaddress {
        192.168.1.200/24
    }

    track_script {
        check_nginx
    }
}
EOF

Step 4 — Configuring Keepalived on the Backup Server

The backup configuration is identical except for the state and priority fields. The backup’s priority of 90 is lower than the master’s 100, so under normal conditions the master holds the VIP. If the master’s Nginx fails, its effective priority drops to 70 (100 − 30), below the backup’s 90, triggering a failover.

# Run on the BACKUP server only
sudo tee /etc/keepalived/keepalived.conf <<'EOF'
global_defs {
    router_id BACKUP_NODE
    script_user root
    enable_script_security
}

vrrp_script check_nginx {
    script "/etc/keepalived/check_nginx.sh"
    interval 2
    weight  -30
    fall    2
    rise    2
}

vrrp_instance VI_1 {
    state BACKUP
    interface ens3
    virtual_router_id 51
    priority 90
    advert_int 1

    authentication {
        auth_type PASS
        auth_pass S3cur3Pass!
    }

    virtual_ipaddress {
        192.168.1.200/24
    }

    track_script {
        check_nginx
    }
}
EOF

Step 5 — Starting Keepalived and Verifying the Virtual IP

Start and enable Keepalived on both servers. The master should immediately claim the virtual IP.

# Run on BOTH servers
sudo systemctl enable --now keepalived
sudo systemctl status keepalived

# On the MASTER — the VIP should appear on the interface
ip addr show ens3

# Verify VIP is reachable from another host
ping -c 3 192.168.1.200
curl http://192.168.1.200/

Step 6 — Testing Failover

Simulate a failure by stopping Nginx on the master. Within approximately 4 seconds (2 intervals × fall 2), the master’s priority drops below the backup’s, and the backup claims the VIP. Restore Nginx to trigger automatic failback.

# On the MASTER — stop nginx to trigger failover
sudo systemctl stop nginx

# On the BACKUP — watch for VIP to appear (within ~4 seconds)
watch -n1 "ip addr show ens3 | grep 192.168.1.200"

# Verify the site is still reachable via the VIP from another host
curl http://192.168.1.200/

# Restore nginx on the master to trigger failback
sudo systemctl start nginx

# Check keepalived logs on both nodes
sudo journalctl -u keepalived -f

Conclusion

You have configured a fully functional VRRP high-availability pair on RHEL 9 using Keepalived. The virtual IP automatically migrates between nodes based on Nginx health, with the master reclaiming the VIP once it recovers. This provides sub-10-second failover times with no external hardware or paid load balancer required. The configuration is production-grade when combined with shared storage or database replication for stateful services.

Next steps: How to Install and Configure Traefik Reverse Proxy on RHEL 9, How to Configure HAProxy for TCP and HTTP Load Balancing on RHEL 9, and How to Set Up Two-Node Pacemaker Clustering on RHEL 9.