Level 2 · Service failover

Keepalived or Pacemaker:
failing a service over in under 10 seconds

Two tools, two scopes, and one question that settles it in a single sentence. Here is what we deploy depending on the service to keep available — and the traps of the two-node cluster, which have not changed in twenty years.

Reading time: 13 min

Not to be confused: two different Corosyncs

The Corosync discussed here is the quorum bus of an service cluster, installed inside machines that know nothing of what runs beneath them. There is another Corosync, the one belonging to the hypervisor cluster: different files, different communication rings, different traps — notably a private link that disappears. If that is the Corosync you are looking for, you want changing or adding a Corosync ring on a Proxmox cluster instead.

1. The principle: an IP that moves

Two machines host the same service. An additional IP address, called floating, belongs to neither of them outright: it is carried by whichever one is currently providing the service. The two nodes continuously exchange short messages; when those stop arriving, the survivor claims the address and announces to the network that it now lives there.

From the client's point of view nothing changes: it keeps talking to the same IP, without knowing there are two machines behind it or which one is answering. That is what distinguishes this approach from a DNS change, which would require every client to look up again and wait for its cache to expire. The mechanism is standardised as VRRP (RFC 5798[1]).

On the BSD side the equivalent is CARP (Common Address Redundancy Protocol). The principle is identical — a shared address, an elected master, periodic announcements — but the implementation is built into the system rather than provided by a third-party daemon. If your firewall or gateway runs OpenBSD, FreeBSD or pfSense, CARP is what we deploy, not keepalived.

Why two protocols for the same need

The reason is legal, not technical, and it deserves to be told accurately. VRRP is an IETF standard (RFC 5798[1]), not a Cisco protocol. What belongs to Cisco is HSRP, its proprietary predecessor — and above all US patent 5,473,599, "Standby Router Protocol", granted on 5 December 1995, which RFC 2281[2] explicitly states "may be applicable".

That patent is what led the OpenBSD project to design CARP: a free implementation, under the BSD licence, built to depend on none of those claims.

The patent expired long ago — it dated from 1995. So the choice between the two is no longer about licensing but about the operating system: keepalived on Linux, CARP on BSD, because each is native where it lives. The history explains why both exist; it no longer decides anything.

It works without a hypervisor too

High availability is often associated with virtualisation, but this setup predates it and remains perfectly valid on bare metal: two physical servers, one shared address, and losing one moves the address to the other within one to three seconds. So this level does absorb hardware failure — what distinguishes it from level 1 is not the kind of failure, but the fact that here we watch the service rather than the machine.

2. Heartbeat: frozen, but still around

If you look for documentation on high availability under Linux, you will inevitably run into Heartbeat. It was the first manager of its kind, within the Linux-HA project, and it powered a great many infrastructures in the 2000s. You still find it in production.

The project is frozen, not erased — and the distinction matters. The last upstream release, 3.0.6, is a decade old, and the project's historical website (linux-ha.org) no longer responds. Debian, however, still packages it: 3.0.6-17 in Trixie. So you can absolutely install it today — but you would be installing a component nobody is developing any more. Development moved to ClusterLabs[3], which maintains Pacemaker and Corosync, and that is the stack that is documented and supported. (Checked 22 August 2026.)

What we do when we find one

The migration is not mechanical, and it is an opportunity to simplify. We look at what the existing configuration actually managed: in the vast majority of cases it only moved an address and checked that a daemon answered — keepalived does that with ten times fewer moving parts. When there really was resource ordering involved, we go to Pacemaker. Porting a fifteen-year-old Heartbeat configuration verbatim into a modern tool would be the worst of both worlds.

3. keepalived: simple, fast, limited

keepalived[4] does one thing and does it well: move an IP address between two nodes, within one to three seconds. It is what we deploy in front of a load balancer, a web server, a firewall or an internal resolver — anywhere there is only one address to carry over.

A minimal configuration that works

Here is the core of what we put on the primary node. The second one is identical apart from two values: state BACKUP and a lower priority.

vrrp_script chk_service {
    script   "/usr/bin/curl -sf http://localhost/health"
    interval 2        # test every 2 s
    timeout  1        # beyond 1 s the test counts as failed
    fall     2        # 2 consecutive failures = service down
    rise     2        # 2 consecutive successes before declaring itself up
    weight  -20       # on failure, lower our own priority
}

vrrp_instance VI_SERVICE {
    state           MASTER
    interface       eth0
    virtual_router_id 51      # same on both nodes, unique on the network
    priority        150       # 100 on the second node
    advert_int      1

    authentication {
        auth_type PASS
        auth_pass change-me
    }

    virtual_ipaddress {
        192.0.2.10/24 dev eth0
    }

    track_script {
        chk_service
    }
}

With these values, failover happens within three to five seconds: two failed checks, then the priority drop that hands over. You can go lower, at the price of greater sensitivity to brief network hiccups — a trade-off we tune service by service.

The block that changes everything: vrrp_script

Without that block, keepalived only watches itself. The node keeps the address as long as its own keepalived daemon runs — even if the service it is supposed to provide died an hour ago. It is the most frequent misconfiguration we find, and a particularly treacherous one: the cluster looks healthy, failover works when you power the machine off to test it, and it never triggers when it is the service that dies. "The node is alive" and "the service answers" are two different questions.

What it does not do

  • Order resources. If a volume must be mounted before a service starts, keepalived does not know it and has no way of learning it.
  • Fence a doubtful node. It moves an address; it does not guarantee the previous holder has stopped working.
  • Migrate state. Sessions in flight on the lost node are gone, unless the application itself knows how to share them.

4. The edge firewall case

This is a keepalived use case people often forget when thinking about application high availability. Two firewalls, one virtual address per side: the internal one is the gateway for workstations, the public-facing one carries the externally reachable address. The network never knows there are two machines.

The trap: failover kills sessions

A firewall keeps the state of every connection crossing it. If that table is not replicated to the second node, it takes over with an empty memory: every established connection, every address translation, every VPN tunnel is treated as unknown and dropped. The firewall fails over in two seconds and users restart everything anyway.

The answer goes by two names depending on the system. On Linux it is conntrackd, which replicates the connection tracking table between both nodes. On BSD it is pfsync, and the CARP + pfsync pairing is arguably the most battle-tested redundant firewall design there is: one carries the address, the other synchronises connection state, and both are built into the system and designed together from the start.

The subject deserves more than these few lines — rule synchronisation, tunnels, failover tests that verify long-lived sessions survive rather than merely that ping works. We will give it its own page. One immediate caveat in the meantime: at a hosting provider, the public address does not move the way it does on a network you own, and the method depends entirely on the provider.

5. Pacemaker + Corosync: the resource manager

When it is no longer about moving an address but about orchestrating a set of things, Pacemaker[5] takes over. The split of responsibilities between the two components is simple, and remembering it avoids a lot of confusion:

Corosync answers "who is alive"

It maintains the list of reachable members and works out who holds the majority. It knows nothing about services: it is a membership bus, nothing more.

Pacemaker answers "who does what"

From that list, it decides where each resource should run, in what order to start them, and what to do when one of them fails.

What it actually brings

  • Resource agents: each service is driven by a standardised script that knows how to start it, stop it and — above all — check its state.
  • Ordering constraints: mount the volume, then start the database, then activate the address — in that order, and in reverse when shutting down.
  • Colocation constraints: this resource must run where that one runs, or on the contrary never in the same place.

STONITH: a cluster without fencing is not a cluster

Before starting a resource elsewhere, you need certainty that it is no longer running where it was. Not a strong presumption: certainty. That is what fencing[6] is for — cutting power to the doubtful node, or forcibly removing it from the network and storage. Without it, a merely unreachable node keeps writing while its replacement writes too, and two processes corrupt the same data each believing it is alone. Many clusters ship with this function disabled because it complicates testing. A cluster without fencing is not a cluster, it is a gamble.

6. Split-brain with two nodes, and the three ways out

Two nodes, a cut link, no dead machine. Each observes that the other has stopped answering and draws the same conclusion: "I am the survivor, I take over." Both activate the shared address. If data is involved, both write. The service can appear to work while the data diverges — and the damage is only discovered afterwards.

The problem is structural: with two, no majority exists. But before concluding that you always need an arbiter, there is one question that actually decides.

Split-brain does not cost the same with and without data

Stateless: two nodes are perfectly fine

Outbound proxy, DNS resolver, NTP, syslog relay, firewall without connection tracking. The worst case is both nodes holding the address at the same time for a few seconds: the network handles it badly, some answers may be duplicated, but nothing is destroyed and the situation resolves itself as soon as the link returns. A two-node cluster is entirely reasonable here, and it is what we deploy most often.

With data: you need a third point of view

Two instances write in parallel, each convinced it is alone, and the two datasets diverge. Nothing raises an alert, the service answers perfectly, and repairing it is not a matter of restarting anything: it is a matter of choosing which version you throw away. That is where — and only where — an independent arbiter becomes indispensable.

When an arbiter is needed, three mechanisms provide one.

A witness

A third voice, hosted on a small machine that carries no workload. It only votes, and that vote is enough to create a majority. It is the cheapest option and the one we favour.

Fencing by force

The first one to successfully shut the other down has won, and it knows it. Effective and unambiguous, but it assumes a control path independent of the failed network — otherwise both fail at the same time.

A third node

The most robust option, and the most expensive: three active members, a majority always computable. That is what we do when the service lends itself to three instances.

What we refuse to do: deliver a two-node cluster carrying data with none of those three mechanisms. It will work perfectly in a demo, pass every machine-shutdown test, and fail precisely on the day the network is cut in the wrong place — silently, which is the worst part.

7. Selection table

The question that separates the two tools fits in one sentence: how many things have to move together? A single address and one service to test: keepalived. An ordered set: Pacemaker.

Your service What we deploy Why
Load balancer, web server keepalived Stateless, a single address to carry over
Firewall, gateway keepalived + conntrackd (Linux)
CARP + pfsync (BSD)
Two addresses, plus the connection table to replicate
DNS resolver, internal NTP keepalived, or several instances The protocol often handles multiple sources by itself
Service with a shared volume Pacemaker + Corosync Start ordering and fencing are indispensable
Several interdependent services Pacemaker + Corosync Ordering and colocation constraints
Database Engine-level replication The problem is data freshness, not the address — see §9

8. Multiple VIPs and round-robin DNS: old-school load distribution

On a two- or three-node cluster there is a setup we deploy often and that gets little attention. Instead of a single floating address, you declare two or three — with crossed priorities, each node carrying its own under normal operation — and publish them together in one DNS record. The client picks at random.

What you get for free

  • • Approximate load distribution
  • • If a node dies, its VIP migrates to a survivor: no service gap, just an imbalance
  • No central component — therefore no new point of failure. A dedicated balancer has to be made redundant itself; this setup does not.

What to know first

  • • DNS performs no application check — hence the importance of the vrrp_script from §3
  • • Client caching and record lifetime delay any correction
  • • Distribution is not weighted: a large server gets as much as a small one
  • • Some resolvers and clients do not rotate at all

It is enough for two or three nodes, internal or moderate traffic, an idempotent service with no sticky sessions. Beyond that you need a real balancer — at layer 4 with IPVS, which keepalived can drive natively, or at layer 7 with HAProxy to route by URL and genuinely test application health: see the 5 levels of load balancing.

9. The database case

Neither keepalived nor Pacemaker solves a database's problem, because the problem is not moving an address. It is guaranteeing that the node taking over genuinely holds up-to-date data. Pointing an address at a lagging replica does not make the service available: it publishes stale data, which is often worse than a clean outage.

Availability is therefore decided at the engine level: either synchronous replication, where a transaction is only committed once several nodes have accepted it, or an orchestrator that watches replication state and only promotes a node that is genuinely current. The network failover comes after, and only after.

It is a field in its own right, with its own trade-offs between write latency and consistency guarantees. We cover it on our dedicated site: Galera cluster for synchronous multi-master replication, and replication-manager for orchestrating failover.

10. What it costs to operate

A cluster doubles the maintenance surface: two systems to update, two configurations to keep identical, and a failover mechanism that has to stay functional between the two. The expensive part is not the installation, it is this.

  • Updates are done node by node, with a deliberate failover in between. It is the ideal opportunity to verify that failover works — provided you use it as such rather than endure it.
  • Configurations diverge silently. A setting applied in a hurry on one node only turns failover into a surprise: the service restarts elsewhere, but not with the same parameters.
  • Failover has to be triggered on purpose. A failover you have never deliberately caused is a hypothesis. We schedule them and measure the actual time achieved, which is the only figure that counts.

All of this is routine operation, included in our managed services plans — from €150 excl. VAT per month per server, reduced from the second node of the same cluster — with 24/7 on-call for whatever automation cannot handle.

Frequently asked questions

Keepalived or Pacemaker: which one should you choose?

The question to ask is not which one is better, but how many things have to move together. If there is a single IP address to move and one service to watch, keepalived is enough and it is the right call: fewer moving parts, fewer ways to fail. As soon as you need to order several resources, guarantee that a filesystem is mounted before a service starts, or fence a failing node by force, Pacemaker becomes necessary. The cost of Pacemaker is not in installing it, it is in operating it.

Can you run a cluster with only two nodes?

Yes, and the answer depends entirely on what the cluster carries. For a stateless service — proxy, DNS resolver, NTP, relay — two nodes are perfectly fine: if the link is cut and both believe they are legitimate, the worst case is an address held twice for a few seconds, annoying but not destructive. As soon as data is involved it is another matter: two instances would write in parallel and the datasets would silently diverge. You then need a third point of view — a lightweight witness that only votes, or a fencing mechanism that settles it by force.

Does a keepalived cluster protect against hardware failure?

Yes, provided both nodes sit on distinct hardware. It is in fact the historical use case, predating virtualisation: two physical servers, one shared address, and losing one moves the address to the other within one to three seconds. What sets this level apart from hypervisor high availability is therefore not the kind of failure but the granularity of monitoring: here it is the service that is watched, not the machine.

What is split-brain, concretely?

It is the situation where two cluster members simultaneously believe they are legitimate. It happens when the network separates them without either one failing: each observes that the other has stopped answering and concludes it must take over. Both then activate the shared address, or worse, mount the same data volume and write to it in parallel. The service can appear to work while the data silently diverges, and that is what makes the scenario so expensive: the damage is only discovered afterwards.

Is Heartbeat still used in 2026?

Yes, in legacy systems — but it is no longer a choice we recommend for a new deployment. Heartbeat was the first high-availability manager on Linux, within the Linux-HA project. Its last upstream release, 3.0.6, is a decade old, and the project's website no longer responds; Debian nevertheless still packages it, which is why you keep running into it. Development itself moved to ClusterLabs, which maintains Pacemaker and Corosync. When we find one in production, we remap what it managed onto keepalived or Pacemaker depending on the real complexity.

What about a MariaDB or PostgreSQL database?

Neither keepalived nor Pacemaker alone is enough, because the problem is no longer moving an address but guaranteeing that the node taking over holds up-to-date data. Pointing the VIP at a lagging replica means publishing stale data. So you need replication at the engine level, synchronous or orchestrated, and that is what decides which node may be promoted. The network failover comes afterwards, and only afterwards.

Can a Pacemaker cluster stretch across two sites?

It is possible but rarely advisable. A cluster assumes frequent exchanges and fast decisions; across two sites, latency makes detection less reliable and an inter-site link failure produces exactly the partition scenario quorum struggles to settle. To cover the loss of a site, you generally change logic: rather than stretching a cluster, you distribute independent instances and let routing or DNS direct traffic to those that answer.

Does your cluster fail over when the service dies, or only when the machine powers off?

It is the first thing we check on an existing cluster. The answer is "only when the machine powers off" more often than anyone would like.

Request an audit