raft: Joining node can self-remove permanently during initial log replay
Describe the bug
The removed checker introduced in #28726 and #29052 causes node to remove itself permanently during initial log replay on slow or CPU-constrained systems.
To Reproduce
Assume a 3-node cluster: vault-0, vault-1 and vault-2. The following sequence of events occurs during initial cluster setup:
vault-0becomes the leader.vault-1joins the cluster.vault-2joins the cluster.Log records from the
vault-0(leader) perspective:$ raft-inspector log LogConfiguration --data-dir data/vault-0 ─── Index 1 (raft/raft.db logs/1) ─── Index: 1 Term: 1 Type: LogConfiguration AppendedAt: 0001-01-01 00:00:00 +0000 UTC Servers: - vault-0 (vault-0:8201) voter ─── Index 38 (raft/raft.db logs/38) ─── Index: 38 Term: 3 Type: LogConfiguration AppendedAt: 2026-08-26 08:03:46.82551766 +0000 UTC (+0s) Servers: - vault-0 (vault-0:8201) voter - vault-1 (vault-1:8201) nonvoter ─── Index 289 (raft/raft.db logs/289) ─── Index: 289 Term: 3 Type: LogConfiguration AppendedAt: 2026-08-26 08:03:58.502445435 +0000 UTC (+11.677s) Servers: - vault-0 (vault-0:8201) voter - vault-1 (vault-1:8201) voter ─── Index 290 (raft/raft.db logs/290) ─── Index: 290 Term: 3 Type: LogConfiguration AppendedAt: 2026-08-26 08:04:17.093120765 +0000 UTC (+30.268s) Servers: - vault-0 (vault-0:8201) voter - vault-1 (vault-1:8201) voter - vault-2 (vault-2:8201) nonvotervault-2receives the member list fromvault-0when joining.vault-2callsraft.BootsrapCluster()at initial unseal which stores a fabricated entry in thevault-2log (link, link).From
raft.dbonvault-2:$ raft-inspector log 1 --data-dir data/vault-2 Index: 1 Term: 1 Type: LogConfiguration AppendedAt: 0001-01-01 00:00:00 +0000 UTC Servers: - vault-0 (vault-0:8201) voter - vault-1 (vault-1:8201) voter - vault-2 (vault-2:8201) nonvoterAt this point
vault-2satisfies the first criteria used for self-removal:vault-2has been present in the configuration (link).Log entries replicate from
vault-0tovault-2. It processes index 38, wherevault-1joined.$ raft-inspector log 38 --data-dir data/vault-2 Index: 38 Term: 3 Type: LogConfiguration AppendedAt: 2026-08-26 08:03:46.82551766 +0000 UTC (+0s) Servers: - vault-0 (vault-0:8201) voter - vault-1 (vault-1:8201) nonvoterAt this point,
vault-2satisfies the second crieria: it is removed in the configuration (link).The periodic "removed checker" runs and marks the node for removal because both criteria are met.
The CPU running
vault-2is busy and/or throttled, log replay is slow, and index 290 (wherevault-2joined) has not been reached yet. As a result, the configurationvault-2has still only listsvault-0andvault-1.Result is permanent shutdown
core: shutting down core: error="node has been removed from the HA cluster"vault-2has persistedremoved=1onraft.dbconf bucket, and it will refuse to work after that (link)$ raft-inspector status --data-dir data/vault-2 | grep "Removed Self" Removed Self: 1
Expected behavior
The cluster should become healthy with all nodes successfully joined.
Environment:
- Vault Server Version: 1.19 and later
Additional context I will come up with repro after a bit.
The tool raft-inspector is my own, you can find it here.
Source: hashicorp/vault