Operator Guide
HA Setup
Commission a provider-neutral two-node cluster. Complete every step with disposable data and certify the pair before using it for production.
Prerequisites
- Two Ubuntu VPSs running the exact same clean Git commit.
- Passwordless SSH from each
deployaccount to the other, with verified host keys. - One public hostname proxied through a dedicated Cloudflare Load Balancer.
- One pool per VPS and an HTTPS monitor on
/ha/ready. - A Cloudflare Workers account with Durable Objects and a narrowly scoped Load Balancing API token.
- Cloudflare Origin CA certificate/key installed independently on each VPS.
- One operator-held snapshot recovery identity with two protected off-VPS copies.
1. Create Cloudflare routing
Cloudflare
- Create a dedicated pool for VPS A containing only its origin address.
- Create a dedicated pool for VPS B containing only its origin address.
- Create an HTTPS monitor for
/ha/readythat requires HTTP 200. - Create a dedicated Load Balancer for the application hostname with both pools.
- Disable session affinity and custom load-balancing rules; the witness owns pool ordering.
- Record the zone, load balancer and two pool IDs. These identifiers are not secrets.
2. Deploy the witness
Workstation
cd infra/cloudflare-ha-witness
npm ci
npx wrangler secret put ADMIN_TOKEN
npx wrangler secret put CLOUDFLARE_API_TOKEN
npm run check
npm run deployADMIN_TOKEN is a new high-entropy bootstrap secret kept on the trusted workstation. CLOUDFLARE_API_TOKEN may edit Load Balancing only for this zone. Record the Worker HTTPS URL; never store either token in Git or on an application VPS.
3. Configure both nodes
VPS SSH
First prove SSH works in each direction:
ssh -o BatchMode=yes PEER_SSH_ALIAS trueMP-OPT
Each wizard shows its public HA age recipient and witness node token once. Store the two bootstrap values securely. After both nodes exist, select Change the peer age encryption recipient on each node and paste only the other node's public recipient.
4. Install origin TLS
Cloudflare
MP-OPT
5. Bootstrap the cluster exactly once
Workstation
curl --fail-with-body -X POST "https://WORKER_URL/v1/clusters/CLUSTER_ID/bootstrap" \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
--data '{"cluster_id":"CLUSTER_ID","node_a_id":"NODE_A","node_a_token":"NODE_A_TOKEN","node_a_pool_id":"NODE_A_POOL_ID","node_b_id":"NODE_B","node_b_token":"NODE_B_TOKEN","node_b_pool_id":"NODE_B_POOL_ID","initial_holder":"NODE_A","zone_id":"ZONE_ID","load_balancer_id":"LOAD_BALANCER_ID"}'The endpoint is creation-only. The response must name the requested holder and generation 1. Wait until routing readiness becomes true.
6. Configure recovery and seed the peer
MP-OPT
- On the holder, configure the shared public snapshot recovery recipient.
- Require matching local/peer recipient SHA-256 values.
- Choose manual portable or automatic SSH recovery storage.
- Select Send a complete verified copy to the peer now.
- Require the sender and receiver to show the same bundle ID and SHA-256.
Only after this succeeds should you begin the formal certification. Automatic failover remains disabled.