1.77.0
Since 1.74, mostly one story: what belongs to the whole cluster and what belongs to one node.
The configuration has two containers now, rather than one container with some objects marked as node-local (1.75.0). The shared sections are what every node has; local is what only this node has — including whole objects, not just settings: the pool, server, health monitor, rule, conditions and certificate publishing this node's own management UI. Sharing became a copy instead of a filter, and strip_local_only, close_local_only, keep_local_only, local_only_ids and the LOCAL_ONLY key went with it — 533 lines out, 345 in.
A 131-rule, 172-pool, 28-listener configuration renders byte-for-byte identically across the change, and a node carrying the old shape upgrades in place.
Two machines using one address are now noticed (1.77.0). Every five minutes the watchdog asks the network who else claims the addresses this node holds; anything that answers is named on the Watchdog page and notified. This is invisible from every layer above the network — the address is configured on the node, the socket is listening on the node, and a client reaches whichever machine won the last ARP exchange — and a cluster that moves addresses about on purpose is where it happens.
Apply checks HAProxy is actually serving afterwards (1.76.0), and puts the previous configuration back if it is not. systemctl returning success means the reload was accepted, not that HAProxy came back: a configuration that passes haproxy -c can still fail to start, because -c never binds a socket.
Also: the whole cluster updates from one node (1.70.0); Web UI access says which names the node actually answers for and where they resolve to (1.72.0, 1.76.1); certificates report names they list but were not issued for (1.71.0); the one-time migrations are gone (1.69.0); and config.json keeps a .bak.
Packages are attached below; the container image is at ghcr.io/avandeputte/haproxy-manager:1.77.0.