The stable endpoint is the product: anatomy of a database VIP

may 9 2026 · 1 min read

Before the Patroni cluster went live, every application in the lab had a database connection string that named a specific node. That was fine until the node changed. A leader switchover, a migration, or even a maintenance restart required touching every service config individually. In a homelab with thirty-plus apps, that is not a plan.

The fix is a floating VIP: a single address that moves with the current primary. Keepalived handles VRRP, so the address lifts off the old leader and attaches to the new one within a few seconds of a health check failing. HAProxy sits behind the VIP and applies Patroni-aware health checks, routing write traffic only to the node Patroni currently marks as primary. The result is a stable hostname, pg.kujo.tech, that every app uses as its connection target. No app ever learns a node address.

For Homelab Control Plane and every other service in the stack, this means zero-touch reconfiguration on a failover. The application reconnects, the pooler (PgBouncer) re-establishes its backend connections, and the only evidence of the event is a small gap in the metrics.

One thing learned later: the VIP carried more than Postgres. HTTP traffic and other services were also routed through the same address. A routine Traefik force-bounce that looked low-risk actually cut every application's database path simultaneously, because they all shared the same failure domain. The lesson is to document what else lives on the VIP before deciding that bouncing it is safe.

See the follow-on Patroni failover AAR for what the first real switchover exercise revealed.