High Availability on Budget Hardware: Configuring Keepalived and HAProxy for Floating IPs on Hetzner Cloud VPS
Introduction: The Challenge of High Availability on Standard Cloud VPS
In modern cloud architecture, achieving high availability (HA) is no longer a luxury—it is a core business requirement. A single point of failure (SPOF) in your infrastructure can lead to costly downtime, damaged reputation, and lost revenue. Typically, enterprise cloud providers offer built-in, hardware-level redundant load balancers that automatically handle traffic failover via a Virtual IP (VIP) or Floating IP. However, cost-effective infrastructure providers, like Hetzner Cloud in certain standard VPS configurations, do not always provide automated layer-2 hardware-assisted IP failover natively out of the box without additional subscription costs or complex setups.
Fortunately, you can achieve enterprise-grade resilience using open-source tools. This technical guide will demonstrate how to configure Keepalived and HAProxy on two standard Hetzner VPS instances to create a self-healing, high-availability cluster using a software-driven Floating IP. By the end of this tutorial, your infrastructure will be capable of detecting a node failure and shifting traffic seamlessly in milliseconds.
Understanding the Architecture: Keepalived and HAProxy
Before diving into the configuration, it is essential to understand the roles of the two primary software components in our high-availability stack:
- 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, ensuring optimal resource utilization and traffic management.
- Keepalived: A routing software based on the Virtual Router Redundancy Protocol (VRRP). Keepalived is responsible for monitoring the health of the nodes and managing the dynamic allocation of the Floating IP. If the primary node goes offline, Keepalived automatically migrates the IP address to the secondary node.
In our architecture, we will utilize two Hetzner Cloud VPS instances, which we will refer to as Node 1 (Primary/Master) and Node 2 (Secondary/Backup). Both nodes will run an instance of HAProxy and Keepalived. The Floating IP will actively route traffic to Node 1. If Node 1 fails, Keepalived will shift the Floating IP to Node 2 instantly.
Prerequisites and Environment Setup
To follow along with this guide, ensure you have the following prerequisites ready:
- Two Ubuntu 22.04 or 24.04 LTS VPS instances hosted on Hetzner Cloud.
- A Failover IP (Floating IP) purchased and assigned to your account within the same Hetzner project/network zone.
- Root or sudo access to both servers.
- Internal private networking enabled between both nodes (e.g.,
10.0.0.11for Node 1 and10.0.0.12for Node 2).
Note: Because Hetzner's network environment is routed (Layer 3) rather than switched (Layer 2), standard VRRP broadcast messages used by Keepalived cannot traverse the public network directly. We will overcome this limitation by using unicast communication over a Hetzner Private Network or using Hetzner's API to reassign the IP. This guide focuses on the robust Unicast Private Network method.
Step 1: Installing Keepalived and HAProxy
First, we must update our system repositories and install the required software packages on both Node 1 and Node 2. Execute the following commands on both servers:
sudo apt update
sudo apt install -y keepalived haproxyOnce installed, stop the services temporarily so we can configure them from scratch:
sudo systemctl stop keepalived
sudo systemctl stop haproxyStep 2: Configuring HAProxy for Load Balancing
Next, we need to configure HAProxy to handle the incoming traffic. The configuration should be identical on both servers, ensuring that whichever node holds the Floating IP can process requests optimally.
Open the HAProxy configuration file on both nodes:
sudo nano /etc/haproxy/haproxy.cfgAppend the following configuration block to the end of the file. This configuration sets up a frontend listening on port 80 and shifts traffic to backend application servers (replace the backend IPs with your actual application or web server IPs):
frontend http_front
bind *:80
stats uri /haproxy?stats
default_backend http_back
backend http_back
balance roundrobin
server app_server1 10.0.0.21:80 check
server app_server2 10.0.0.22:80 checkSave and close the file. To allow HAProxy to bind to the Floating IP address (which may not always be assigned to the local network interface), we must enable non-local binding in the Linux kernel configuration. Run the following command on both servers:echo "net.ipv4.ip_nonlocal_bind=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -pStep 3: Configuring Keepalived with Unicast VRRP
Now, we will configure Keepalived. This is where the configuration diverges between the Master and Backup nodes. We will configure Keepalived to communicate via unicast over the Hetzner Private Network interface (e.g., enp7s0 or similar; verify your interface name using ip a).
On Node 1 (Master Server)
Create or overwrite the configuration file:
sudo nano /etc/keepalived/keepalived.confInsert the following configuration:
vrrp_script check_haproxy {
script "/usr/bin/killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_1 {
state MASTER
interface enp7s0
virtual_router_id 51
priority 101
advert_int 1
authentication {
auth_type PASS
auth_pass SecureSecret123
}
unicast_src_ip 10.0.0.11
unicast_peer {
10.0.0.12
}
virtual_ipaddress {
/32
}
track_script {
check_haproxy
}
} On Node 2 (Backup Server)
Create the configuration file on the second node:
sudo nano /etc/keepalived/keepalived.confInsert the backup configuration, noting the lower priority value and reversed unicast IPs:
vrrp_script check_haproxy {
script "/usr/bin/killall -0 haproxy"
interval 2
weight 2
}
vrrp_instance VI_1 {
state BACKUP
interface enp7s0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass SecureSecret123
}
unicast_src_ip 10.0.0.12
unicast_peer {
10.0.0.11
}
virtual_ipaddress {
/32
}
track_script {
check_haproxy
}
} Step 4: Integrating with Hetzner API (Failover Script)
Because Hetzner Cloud infrastructure requires a control-plane API call to re-route a Floating IP to a new MAC address, relying purely on standard VRRP packets inside a private network isn't always enough to update Hetzner's external routing tables. We need a script that calls the Hetzner API during a failover event.
Create a script called /etc/keepalived/failover.sh on both nodes:
sudo nano /etc/keepalived/failover.shAdd a curl command that updates the Floating IP assignment via the Hetzner Cloud API using your API token and server ID. (For production, implement a Python or Bash script that triggers on Keepalived's notify_master state change).
Once all configurations are updated, start and enable the services on both nodes:
sudo systemctl enable --now haproxy
sudo systemctl enable --now keepalivedStep 5: Testing and Verification
To verify that your high-availability configuration is operating correctly, run the following command on Node 1:
ip addr show enp7s0You should see your Hetzner Floating IP bound to the interface. Now, simulate a critical failure on Node 1 by stopping the HAProxy or Keepalived service:
sudo systemctl stop keepalivedCheck the network interface on Node 2. Within milliseconds, Node 2 should assume the MASTER role, and the Floating IP will seamlessly appear on Node 2's network interface. Your applications remain online, and your business avoids downtime.
Conclusion
Building high-availability clusters does not require expensive hardware contracts or enterprise cloud tiers. By leveraging Keepalived for intelligent health checking and HAProxy for robust load balancing, you can deploy a reliable, production-ready infrastructure on budget-friendly Hetzner Cloud VPS instances. Implementing this architecture protects your operations from hardware anomalies, network degradation, and localized software failures, delivering enterprise-level uptime to your end-users.
