How to Set Up a Highly Available Web Stack with Keepalived on RHEL 7
High availability eliminates single points of failure by ensuring that if one server goes down, another takes over seamlessly. For web servers, one of the most common HA patterns is a Virtual IP (VIP) address that floats between two or more nodes using the VRRP protocol — Virtual Router Redundancy Protocol. Keepalived implements VRRP on Linux and is the standard solution for floating VIPs in RHEL 7 environments. When the active (MASTER) server becomes unreachable, the standby (BACKUP) server claims the VIP within one or two seconds, and clients connecting to that IP experience only a brief TCP timeout rather than a prolonged outage. This guide walks through a complete Keepalived setup with Nginx health checking, failover testing, and VIP binding configuration.
Prerequisites
- Two RHEL 7 servers on the same Layer 2 network segment (they must be able to send multicast or unicast VRRP packets to each other)
- A Virtual IP address in the same subnet as the servers’ physical NICs (e.g., server1: 192.168.1.10, server2: 192.168.1.11, VIP: 192.168.1.100)
- Nginx installed on both servers:
yum install -y nginx - Root or sudo access on both servers
- Firewall rules allowing VRRP traffic (protocol 112) between the two servers
Step 1: Installing Keepalived on Both Servers
Run the following on both servers:
yum install -y keepalived
Verify the installation:
keepalived --version
# Keepalived v1.3.x (DD/MM/YYYY)
systemctl enable keepalived
Open the required firewall ports on both servers. VRRP uses protocol 112 (not TCP or UDP):
firewall-cmd --permanent --add-rich-rule='rule protocol value="vrrp" accept'
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
Step 2: Configuring the MASTER Node
On server1 (the primary/MASTER), edit the Keepalived configuration file:
cp /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.orig
vi /etc/keepalived/keepalived.conf
! Keepalived Configuration — MASTER node (server1: 192.168.1.10)
global_defs {
router_id server1
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
}
vrrp_script check_nginx {
script "/usr/bin/systemctl is-active nginx"
interval 2 # check every 2 seconds
weight -20 # reduce priority by 20 if check fails
fall 2 # require 2 consecutive failures before reducing priority
rise 2 # require 2 successes before restoring priority
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass S3cur3P@ss
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip
}
track_script {
check_nginx
}
}
Key parameters explained:
- state MASTER — this node starts as the active VIP holder
- interface eth0 — the NIC on which VRRP packets are sent and the VIP is bound; replace with your actual interface name (check with
ip link show) - virtual_router_id 51 — must be identical on MASTER and BACKUP; identifies this VRRP group (1–255)
- priority 100 — MASTER should have higher priority than BACKUP (e.g., MASTER=100, BACKUP=90)
- advert_int 1 — send VRRP advertisement every 1 second; BACKUP waits 3× this before taking over
- auth_type PASS — simple password authentication between MASTER and BACKUP; must match on both nodes
- virtual_ipaddress — the floating VIP; Keepalived will add/remove this with
ip addr add/del
Step 3: Configuring the BACKUP Node
On server2 (the standby/BACKUP), create an identical configuration with two differences: state BACKUP and a lower priority:
vi /etc/keepalived/keepalived.conf
! Keepalived Configuration — BACKUP node (server2: 192.168.1.11)
global_defs {
router_id server2
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
}
vrrp_script check_nginx {
script "/usr/bin/systemctl is-active nginx"
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 S3cur3P@ss
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip
}
track_script {
check_nginx
}
}
Step 4: Enabling ip_nonlocal_bind for Nginx on the VIP
When Nginx starts, the VIP may not yet be assigned to the current server (especially at boot time when VRRP negotiation has not completed). Without the ip_nonlocal_bind kernel parameter, Nginx will fail to bind to the VIP address and refuse to start.
Enable this on both servers:
sysctl -w net.ipv4.ip_nonlocal_bind=1
echo 'net.ipv4.ip_nonlocal_bind = 1' >> /etc/sysctl.d/99-keepalived.conf
sysctl -p /etc/sysctl.d/99-keepalived.conf
Configure Nginx to listen on the VIP on both servers. Edit /etc/nginx/nginx.conf or a vhost file:
server {
listen 192.168.1.100:80;
listen 80;
server_name example.com;
root /var/www/html;
index index.html;
}
Start Nginx on both servers:
systemctl enable nginx --now
systemctl status nginx
Step 5: Starting Keepalived and Verifying VIP Assignment
Start Keepalived on both servers (start MASTER first):
# On server1 (MASTER)
systemctl start keepalived
systemctl status keepalived
# On server2 (BACKUP)
systemctl start keepalived
systemctl status keepalived
Verify the VIP is assigned to the MASTER:
# On server1 — should show 192.168.1.100
ip addr show eth0
# On server2 — should NOT show 192.168.1.100
ip addr show eth0
Check Keepalived logs for VRRP state transitions:
journalctl -u keepalived -n 50 --no-pager
Look for lines like:
VRRP_Instance(VI_1) Transition to MASTER STATE
VRRP_Instance(VI_1) Sending gratuitous ARPs on eth0 for 192.168.1.100
Step 6: Testing Failover
Simulate a MASTER failure by stopping Keepalived on server1 while watching the VIP on server2:
# Terminal 1 — watch VIP on server2
watch -n 0.5 ip addr show eth0
# Terminal 2 — stop keepalived on server1
systemctl stop keepalived
Within 3 seconds (3× advert_int), server2 should claim the VIP. Restore the MASTER:
systemctl start keepalived
The VIP will return to server1 because its priority (100) is higher than server2’s (90). To prevent preemption and keep server2 as MASTER until explicitly failed back, add nopreempt to the BACKUP configuration’s vrrp_instance block.
Test the Nginx health check failover by stopping Nginx on the MASTER:
systemctl stop nginx
The vrrp_script check_nginx will detect the failure (after 2 intervals = 4 seconds), subtract 20 from the MASTER’s priority (making it 80, below the BACKUP’s 90), and trigger a VIP migration to server2. Restart Nginx to restore:
systemctl start nginx
Conclusion
Keepalived with VRRP provides a robust, lightweight high availability solution for RHEL 7 web stacks that requires no shared storage, no cluster middleware, and no application changes. By combining the floating VIP with a vrrp_script that monitors Nginx health, you get automatic failover both when the entire server goes down and when just the web server process fails. The ip_nonlocal_bind parameter ensures smooth startup regardless of which node currently holds the VIP. With failover times under five seconds and the ability to host the entire setup on two commodity servers, Keepalived is one of the most cost-effective HA tools available in the RHEL 7 ecosystem.