What clustering gives you
A standalone appliance runs a single database. Clustering joins 2 or more appliances into one logical system so that a node failure does not take the service down or lose data:- Single writable primary + synchronous standbys — one node is the primary; every other node holds a full, continuously-streamed replica. Writes are acknowledged only once a synchronous standby has them, so an intra-datacenter failover loses zero committed data (RPO 0).
- Automatic failover — if the primary fails, a healthy standby is promoted automatically (typically within ~10 seconds) and the ingress address follows it. No manual intervention.
- Stable ingress — users always reach one address (a floating VIP, your load balancer, or a DNS name) that survives every failover.
- Optional shared session/job tier (P2) — clustered Redis + RabbitMQ so user sessions and in-flight jobs also survive a node loss, not just committed data.
Prerequisites
Work through this checklist before you start. A missing prerequisite is the most common cause of a failed join.License — HA clustering enabled
License — HA clustering enabled
Node count — 3 recommended, odd numbers
Node count — 3 recommended, odd numbers
- 1 = standalone (no HA).
- 2 = supported, but a two-node cluster cannot form a quorum on its own — run 3 for automatic failover without split-brain risk.
- 3 (recommended) = one primary + two standbys; tolerates the loss of any single node with automatic failover.
Identical build + resources
Identical build + resources
Time sync (NTP) — a hard requirement
Time sync (NTP) — a hard requirement
Create cluster / Join will refuse to run on an
unsynchronized clock. (A lab-only override exists in the wizard for
disconnected test benches — never use it in production.)Unique hostname per node
Unique hostname per node
enclave,
so you must rename each node before it joins (for example enclave-01,
enclave-02, enclave-03). Set it on the console with
cluster node-name enclave-02, or via the hostname control on the first-run
screen.Network — reachability, ports & the VIP
Network — reachability, ports & the VIP
- All nodes must reach each other over the network. For the built-in VIP ingress mode, all nodes must be on the same subnet (Layer 2); for cross-subnet or cross-site, use a load balancer or DNS ingress instead.
- Reserve a spare IP on the subnet for the VIP (the floating address users hit) — it must not be assigned to any host.
-
Open these ports between nodes (the appliance firewall opens them on
cluster activation, but any upstream network ACLs must allow them too):
A recent backup
A recent backup
Choose an ingress mode
Ingress is the stable front-door address users always hit. Pick one up front — you set it when you create the cluster.Step-by-step: build the cluster
You can do everything from the admin GUI (recommended). The console is the break-glass equivalent for an operator without browser access. The GUI stages each action and runs it on the host; the console runs the same verbs directly.On the FIRST node — create the cluster
- Admin GUI
- Console
- Go to Admin → Cluster / High Availability.
- Click Create cluster.
- Choose the ingress mode and, for VIP, enter the reserved VIP address and the full list of node IPs.
- Confirm. The board turns green with this node shown as Leader and the status Healthy — single node (no standby yet).
On the primary — mint a join token for the new node
- Admin GUI
- Console
- On the primary’s Cluster page, click Add node.
- Copy the sealed join bundle and its one-time passphrase. Send the bundle and the passphrase to the new node over separate channels.
On the NEW node — join the cluster
- Admin GUI
- Console
- On the new node, go to Admin → Cluster / High Availability.
- Click Join existing cluster.
- Paste the join bundle and the passphrase, and enter the full list of node IPs.
- Submit. A live progress panel shows each phase: Enrolling → Awaiting operator approval → Adopting → Registering → Starting → Streaming → Verifying.
On the primary — approve the new node
- Admin GUI
- Console
- On the primary, a Nodes awaiting approval card appears with the node’s CSR fingerprint.
- Verify that fingerprint matches exactly the one shown on the joining node, then click Approve.
CLUSTER_ENROLL_AUTO_APPROVE=false on the primary before
minting tokens — the node then waits at Awaiting approval until you click
Approve.Verify the node is streaming
- Admin GUI
- Console
enclave-01 — Leader — Up and
enclave-02 — Sync Standby — Up — Lag 0. Status shows Healthy · Quorum
2/2 OK.Repeat for each additional node
Optional: enable the shared session/job tier (P2)
Phase 1 (above) makes your database highly available. P2 additionally makes Redis (sessions, one-time codes, rate limits) and RabbitMQ (in-flight background jobs) cluster-wide, so a failover no longer logs users out or drops running jobs.Enable on the leader first, then every other node
- Admin GUI
- Console
Verify
Test a failover
Once you have 3 healthy nodes, prove the cluster does its job:- Power off the primary node.
- Within ~10 seconds a standby is promoted to primary and the VIP moves to it. Users on the ingress address stay connected (or reconnect immediately).
- Confirm on any surviving node:
cluster statusshows a new Leader on a higher timeline, and the remaining standby re-pairs to it. - Power the old primary back on — it rejoins automatically as a standby and catches up.
Day-2 operations
cluster rollback is the safe way to take a node out of a cluster — it
restores standalone operation and keeps the node serving. Use it before you
re-cluster a node, or to wind a cluster down.Removing and decommissioning a node
Remove takes a node out of the cluster permanently (hardware retirement, replacement, or recovering from an unhealthy member). It is a three-part operation, and the last part is yours:Remove it from the cluster
Power the node off
Wipe or destroy the machine
cluster rollback), or reinstall it, and join it again with a fresh token —
a legitimate re-join under the same name automatically lifts the fence.Security model
- Mutual TLS everywhere — every inter-node channel (database replication, etcd, the Patroni control API) is authenticated with certificates from an internal cluster CA. An on-network stranger without a CA-signed certificate cannot read the coordination store or register as a member.
- CA private key stays on the host — it is never mounted into a container, so a compromised application container cannot mint cluster certificates. The key lives only on the node where the cluster was created; it is never copied to other members or into join bundles. You can run Add node and approvals from any node’s page — the certificate signing itself is always performed by the cluster-creation node automatically. Protect that node’s backups: if it is permanently lost, new nodes can no longer be enrolled into this cluster’s PKI (existing members keep working).
- Sealed, single-use join tokens — the join bundle is encrypted and authenticated under a one-time passphrase (sent over a separate channel), carries an authenticated 15-minute expiry, and does not contain the CA key.
- Post-quantum ready — node enrollment is authorized with a hybrid (classical + post-quantum, Ed25519 + ML-DSA-65) attestation, and inter-node transport negotiates a hybrid key exchange where the components support it.
- Every action is audited — create/join/approve/reject/switchover and failovers are written to the tamper-evident audit log, and the audit hash-chain is re-anchored on every failover so integrity survives a primary change.
Troubleshooting
Create cluster says the feature isn't licensed
Create cluster says the feature isn't licensed
Join is refused for an unsynchronized clock
Join is refused for an unsynchronized clock
The join token expired / won't work twice
The join token expired / won't work twice
cluster join-token) and join again.Hostname already a member / duplicate name
Hostname already a member / duplicate name
cluster node-name enclave-02, then re-run the join.A join failed partway
A join failed partway
/var/log/enclave/cluster/.The joined node's cluster page shows an error / won't load
The joined node's cluster page shows an error / won't load
Add node fails with 'cluster PKI missing' (older releases)
Add node fails with 'cluster PKI missing' (older releases)
A removed node still appears on the board
A removed node still appears on the board
Day-2: certificates, key recovery & revocation
The cluster runs its own internal certificate authority (CA) for the encrypted channels between nodes. The CA private key lives only on the node you created the cluster on. These day-2 operations keep that trust healthy over the appliance’s life.Automatic certificate renewal
CA-key escrow & recovery
Revoke a node's certificate
cluster-crl-enforce on (it validates the CRL first, then applies on the
next stack restart) — see the certificate-expiry runbook.Verified switchover & self-healing rolls
Set up CA-key escrow (once, soon after creating the cluster)
Choose custodians and a quorum
age key pair on their own machine and shares only their public recipient
(age1…) — the private key stays with them, off-box.Create the escrow bundle on the CA-holding node
cluster-ca-escrow -k K -n N -o <bundle> <recipient…>. It writes an off-box bundle
(the CA key encrypted under a random secret, plus one sealed share per custodian). The
plaintext CA key never leaves the node and is never seen by the application.Distribute and store
share-<i>.age. On the Cluster admin page, confirm the badge reads
CA-key escrow: Configured (K-of-N).Rehearse the recovery drill
cluster-ca-recover --dry-run — it reconstructs and verifies the key against the CA
without installing anything.Recover the CA key onto a replacement node
Confirm the loss
Collect a quorum of shares
age -d -i <identity> share-<i>.age)
and returns the decrypted value. Assemble them, one per line, into a shares file.Rebuild the key on the new holder
cluster-ca-recover -b <bundle> -s <shares>. It verifies
the shares against the bundle and installs the CA key only on a passing check. That
node is now the CA holder; enrollments and renewals resume automatically.Remove a node and re-join it later
Removing and re-adding a node is a console procedure run on a healthy member — the appliance cannot stop a remote node’s stack, so a removed box is powered off (or rolled back to standalone) and then re-joined with a fresh certificate.Remove the node (on a healthy member)
cluster-remove <node> deregisters it from etcd + Patroni, revokes its enrollment and
certificate, and fences its database access. This works whether the node is still live
or already gone (revoked/evicted/powered off) — in the already-gone case it also
cleans up the stale etcd membership for you, so there is never any manual step.Return the removed box to standalone (on that box)
cluster-rollback. It becomes a standalone appliance on its own IP again.Re-join it (Add node)
cluster-join-token (or the Cluster → Add node button),
run cluster-join <bundle> <all-node-ips> on the box, then approve its CSR
fingerprint on the Cluster board. It receives a fresh, non-revoked certificate —
accepted even with strict CRL enforcement on — and streams back into the cluster.support-bundle produces a secret-scrubbed tarball that now includes
per-container application logs and full cluster PKI state (etcd membership, the CRL, and the
certificate/enrollment ledger) — everything support needs to diagnose a cluster or app-tier
fault off-box. For a deeper session, raise verbosity with log-level debug (recreates just
the backend) and set it back to info afterwards.