/primary on Patroni’s REST port 8008. Settings (core_patroni_dcs in
infra/ansible/roles/core/defaults/main.yml): ttl 30 s, loop_wait 10 s,
synchronous_mode on, synchronous_mode_strict off, maximum_lag_on_failover 1 MiB,
failsafe_mode on. The cluster is named sa-<env> (sa-staging, sa-production).
etcd down is not a database outage. With failsafe_mode, a leader that loses etcd quorum
(EtcdNoLeader, two edge nodes down, the edge tier cut off from the core tier) stays primary as
long as it reaches every member listed in Patroni’s /failsafe through the REST API; no
failover can happen meanwhile. Restore etcd first (edge-node-failure.md
step 4); the database keeps serving.
Alerts that lead here: PatroniNoLeader, ReplicationLagHigh, EtcdNoLeader,
EtcdMembersDown, WALArchivingFailing (alerts.md). Conventions are in
overview.md.
Ingestion does not depend on the database: gateways and collectors keep answering and the queues
hold the work (queue-backlog-and-dlq.md). Dashboards, the control
plane, the writer and jobs stop while no leader exists. The configured window is at most about
40 s (TTL plus one loop); it has not been measured.
Automatic failover: check it happened
-
On either core node:
Expected after a leader loss: the former replica shows
Leaderandrunning, the lost member is missing orstopped. Timeline (TL) is one higher than before. -
Check HAProxy routes to it: on each core node,
Expected:
f. The command asks for the superuser password; it is in/etc/veristar/env/platformctl.envasPLATFORM_DB_ADMIN_PASSWORD. Without automatic failover (noLeaderfor more than a minute), check etcd first: Patroni cannot take the leader key without two etcd members (edge-node-failure.md). -
Check the services reconnected:
sudo docker compose pson both core nodes, theServiceDownandUmamiHeartbeatFailingalerts resolve, and the console loads.
archive_timeout 60 s). Bring the other member back promptly.
Planned switchover
For maintenance on the leader’s node.deploy.yml does the same before it recreates a leader’s
postgres container (infra/ansible/playbooks/tasks/switchover.yml).
staging-core-1, staging-core-2). The candidate must be the
Sync Standby in streaming state. Expected: the list shows the candidate as Leader within
a few seconds and no committed transaction is lost.
Replica rebuild
The replica is stopped, far behind, or its data is damaged (ReplicationLagHigh that does not
recover, start failed in the list).
-
Start it first:
sudo docker compose up -d postgreson its node, thenpatronictl list. Patroni usespg_rewind(use_pg_rewind: true), so a former leader usually rejoins as a replica by itself. -
If it does not, have Patroni rebuild it from the leader (this deletes the replica’s data
directory):
Expected: the member goes through
creating replicatostreaming, andLag in MBreaches 0. On production the cluster issa-production. -
Confirm the replica is synchronous again: the list shows
Sync Standby.
A lost core node
Replace the server with OpenTofu as in edge-node-failure.md (“A node that is gone for good”, withmodule.node["staging-core-<n>"]), then
make staging-deploy SA_IMAGE_TAG=<release tag> SA_RELEASE_MANIFEST=<release-images.json>. Patroni on the new node finds the cluster in
etcd and creates a replica from the leader.
Exercised
Not yet exercised. The phase 16 database-leader drill (tests/drills/db-leader.sh) is
written but was not run: the shared host stopped being usable before it
(../reference/drills/2026-11-13.md). The switchover task
of deploy.yml has not run either, because the rolling-deploy drill did not run. What has run:
the local converge with two core nodes showed core 2 Leader and core 1 Sync Standby,
streaming, lag 0 (same record, “Topology checks”). In phase 19 the steps could not be executed:
the development stack has a single PostgreSQL without Patroni, and the multi-node topology may
not run on the shared host. This runbook must be executed on staging (inputs I5, I13, I14, I15)
before it is relied on.