High Availability on Hetzner: Configuring Keepalived and HAProxy for Virtual IP Failover Without Hardware Support
Introduction to High Availability in Cloud Environments
In modern enterprise architecture, ensuring the continuous availability of web applications is a critical requirement. A single point of failure (SPOF) in your infrastructure can lead to costly downtime, loss of revenue, and damage to brand reputation. To mitigate this risk, system architects implement high-availability (HA) clusters.
Typically, achieveing high availability involves deploying a load balancer that routes traffic to multiple backend servers. However, the load balancer itself can become a single point of failure. This is where Keepalived and HAProxy come into play. By pairing these two open-source technologies, businesses can create a redundant, self-healing routing tier. While many cloud providers offer native, hardware-supported Floating IPs or managed load balancers, certain environments—such as specific standard VPS tiers or legacy setups on Hetzner Cloud—require a software-driven approach to IP failover. This guide provides a comprehensive, step-by-step walkthrough to configuring a Virtual IP (VIP) across two Hetzner VPS instances entirely through software definitions.
Understanding the Architecture: HAProxy and Keepalived
Before diving into the implementation phase, it is essential to understand how these two components interact to guarantee uptime.
- HAProxy (High Availability Proxy): A high-performance, open-source load balancer and proxy server for TCP and HTTP-based applications. It distributes incoming traffic across multiple backend application servers based on defined algorithms (e.g., Round Robin, Least Connections).
- Keepalived: A routing software based on the Virtual Router Redundancy Protocol (VRRP). It monitors the health of the HAProxy instances and the servers themselves. If the primary load balancer fails, Keepalived automatically shifts the Virtual IP (VIP) to the secondary load balancer, ensuring seamless traffic transition.
In a standard environment with hardware support, the cloud provider's API or router instantly reassigns the public IP. On Hetzner VPS instances without native hardware-level floating IP support for the specific subnet, we simulate this by utilizing Keepalived to manage a private or public binding via VRRP, complemented by Hetzner's API scripts to reassign the IP dynamically at the software layer.
Prerequisites and Environment Setup
To follow this guide, you will need two Hetzner Cloud VPS instances located within the same private network (vNet). For the purposes of this demonstration, we will use the following baseline configuration:
- Node 1 (Master): Ubuntu 22.04 LTS, Private IP:
10.0.0.11 - Node 2 (Backup): Ubuntu 22.04 LTS, Private IP:
10.0.0.12 - Shared Virtual IP (VIP):
10.0.0.10(or a designated Hetzner Floating IP managed via API) - Access Privileges: Root or sudo access on both nodes.
- Hetzner API Token: A read/write API token generated from the Hetzner Cloud Console to facilitate IP reassignment.
Step 1: Installing and Configuring HAProxy
First, we must install HAProxy on both nodes. The configuration should be identical on both servers to ensure consistent load balancing behavior regardless of which node is actively handling traffic.
Execute the following commands on both Node 1 and Node 2 to update the package repository and install HAProxy:
sudo apt update
sudo apt install haproxy -y
Once installed, open the HAProxy configuration file located at /etc/haproxy/haproxy.cfg and append your frontend and backend configurations. Below is an enterprise-grade example configured to balance HTTP traffic:
frontend http_front
bind 10.0.0.10:80
mode http
default_backend web_servers
backend web_servers
mode http
balance roundrobin
option httpchk GET /health
server web01 192.168.1.50:80 check
server web02 192.168.1.51:80 check
Note: The bind directive points to the Virtual IP address (10.0.0.10). Since this IP is not yet assigned to the network interface, Linux will normally prevent HAProxy from starting. To resolve this, we must enable non-local binding by adding the following line to /etc/sysctl.conf on both nodes:
net.ipv4.ip_nonlocal_bind=1
Apply the changes immediately using sudo sysctl -p, then start and enable HAProxy:
sudo systemctl enable haproxy
sudo systemctl start haproxy
Step 2: Installing Keepalived and Configuring VRRP
With HAProxy prepared to receive traffic on the VIP, we deploy Keepalived to manage the allocation of that IP. Install Keepalived on both instances:
sudo apt install keepalived -y
Configuring the Master Node (Node 1)
Create or overwrite the configuration file at /etc/keepalived/keepalived.conf on Node 1:
vrrp_script check_haproxy {
script "/usr/bin/killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 101
advert_int 1
authentication {
auth_type PASS
auth_pass Secr3tPa55w0rd
}
virtual_ipaddress {
10.0.0.10/24
}
track_script {
check_haproxy
}
}
Configuring the Backup Node (Node 2)
On Node 2, the configuration mirrors the Master but with adjustments to the state and priority parameters to ensure proper election dynamics:
vrrp_script check_haproxy {
script "/usr/bin/killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass Secr3tPa55w0rd
}
virtual_ipaddress {
10.0.0.10/24
}
track_script {
check_haproxy
}
}
Step 3: Integrating Hetzner API for Non-Hardware Floating IP Routing
Because the Hetzner underlying infrastructure does not automatically route traffic for a arbitrary virtual IP assigned via VRRP over standard private networks without dedicated cloud routing configuration, we must hook into Keepalived's state transition script. This script triggers a call to Hetzner's Cloud API to instantly reassign the Floating IP or route to the active node.
Create a script named /etc/keepalived/failover.sh on both nodes:
#!/bin/bash
TYPE=$1
NAME=$2
STATE=$3
TOKEN="YOUR_HETZNER_API_TOKEN"
FLOATING_IP_ID="YOUR_FLOATING_IP_ID"
SERVER_ID=$(curl -s [http://169.254.169.254/hetzner/v1/metadata/instance-id](http://169.254.169.254/hetzner/v1/metadata/instance-id))
case $STATE in
"MASTER")
curl -X POST \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"server\":$SERVER_ID}" \
"[https://api.hetzner.cloud/v1/floating_ips/$FLOATING_IP_ID/actions/assign](https://api.hetzner.cloud/v1/floating_ips/$FLOATING_IP_ID/actions/assign)"
;;
"BACKUP"|"FAULT")
# Optional: Log or handle teardown steps
;;
esac
Make the script executable: sudo chmod +x /etc/keepalived/failover.sh. Then, update your keepalived.conf file on both servers to add the notification hook within the vrrp_instance block:
notify /etc/keepalived/failover.sh
Restart Keepalived on both instances to apply the configurations:
sudo systemctl enable keepalived
sudo systemctl restart keepalived
Step 4: Verification and Failover Testing
Validation is crucial to verify that high availability works seamlessly under failure conditions. Run the following command on the Master node to verify it has claimed the VIP:
ip addr show eth0
You should see the 10.0.0.10 address listed. Now, simulate a catastrophic failure by stopping the HAProxy service on the Master node:
sudo systemctl stop haproxy
Monitor the system logs on the Backup node (tail -f /var/log/syslog). You will observe Keepalived detecting the failure, transitioning Node 2 to the MASTER state, executing the failover script, and successfully claiming the Virtual IP. Your application remains accessible with minimal to zero packet drop, protecting your users from operational downtime.
Conclusion
Implementing a software-defined high-availability stack using Keepalived and HAProxy is an excellent, cost-effective method for establishing redundancy on Hetzner Cloud VPS instances. By combining VRRP tracking with custom API integrations, you overcome infrastructure limitations, creating an enterprise-grade infrastructure layer capable of weathering server crashes and network anomalies without degrading the end-user experience.
