Skip to main content
RunBook Academy

Proxmox VEXI · ClusteringCluster lifecycle

Joining, removing, and replacing nodes

Intermediate⏱ ~26 minpvecm

What you'll learn

  • Join a node to an existing cluster and verify the join rather than assume it
  • Remove a node cleanly, in the order that preserves what you need
  • Replace a permanently failed node, including recovering its guest configurations
  • Explain why a removed node must be reinstalled before it is used again

Prerequisites

Verified against Proxmox VE 9.2.4 · Proxmox Backup Server 4.2.5 · Ceph Squid / Tentacle · Debian 13 (Trixie) · Linux kernel 7.0 (PVE 9.2 default) · 2026-08-12

Not yet marked complete on this device.

Why this matters in production

Adding a node is routine and forgiving. Removing one contains the irreversible steps, and the ordering matters more than the commands do: get it wrong and a node’s guest configurations are gone before you have finished reading the confirmation prompt.

Which command runs where

This is the first thing to get straight, because the two commands look similar and are not interchangeable.

CommandRun onPurpose
pvecm create <name>The first nodeCreates the cluster
pvecm add <cluster-node>The joining nodeJoins this node to an existing cluster
pvecm addnode <node>An existing cluster nodeInternal — invoked by the join process, not by you
pvecm delnode <node>An existing cluster node, not the one being removedRemoves a node
pvecm status / pvecm nodesAny nodeRead state
pvecm qdevice setup / removeAn existing cluster nodeQDevice lifecycle

The pairing to remember: you add from the new node and you delete from an old one. Running either from the wrong place is where most of the confusion in this procedure comes from.

Before you join anything

Read-only / Safethe pre-join checklist, as commands
set -euo pipefail
PEER=pve-01.example.com
PEER_IP=192.0.2.11

# 1. Same PVE version as the cluster. A version mismatch is not supported.
pveversion -v | head -3

# 2. Forward and reverse resolution agree, for this node and the peer.
hostname -f
getent hosts "$(hostname -f)"
getent hosts "$PEER"
getent hosts "$PEER_IP"

# 3. Time is synchronised. Corosync will not tolerate meaningful skew.
chronyc tracking | grep -E 'Reference ID|System time|Leap status'

# 4. Reachability on the ports that matter: SSH for the join itself,
#    8006 for the API, and the corosync range for cluster traffic.
nc -z -w3 "$PEER_IP" 22   && echo 'ssh ok'
nc -z -w3 "$PEER_IP" 8006 && echo 'api ok'

# 5. THIS NODE MUST BE EMPTY. Joining destroys its local configuration.
qm list
pct list

Joining

The modern path is through the GUI: on an existing node, Datacenter → Cluster → Join Information, copy the blob, and paste it into Join Cluster on the new node. It carries the address, the fingerprint and the peer list, which removes the three things people mistype.

From the command line, on the new node:

Cluster-wide riskjoining from the CLI
set -euo pipefail
PEER_IP=192.0.2.11

# Run on the NEW node. It will ask for the root password of the peer.
# --fingerprint comes from the join information on the existing cluster.
pvecm add "$PEER_IP" \
--fingerprint 'AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99'

# On a multi-link cluster, give this node an address on every link, or it
# joins with one link and the redundancy you think you have is incomplete.
#   pvecm add "$PEER_IP" --link0 10.0.0.14 --link1 10.0.1.14

# --use_ssh forces the older SSH-based join path, which is the fallback when
# the API join fails.
Read-only / Safeverifying the join, rather than assuming it
set -euo pipefail

# 1. The node is a member and the vote count has increased.
pvecm status | grep -E 'Quorate|Expected votes|Total votes'
pvecm nodes

# 2. Every link is up to every peer - not just link 0.
corosync-cfgtool -n

# 3. pmxcfs has synchronised: the new node can see the cluster's guests
#    and storage, which it could not before.
cat /etc/pve/.members
pvesm status

# 4. A write from the new node reaches the cluster.
PROBE=/etc/pve/.join-probe
date -Is > "$PROBE" && cat "$PROBE" && rm -f "$PROBE"

# 5. Certificates are consistent. If the GUI on one node cannot proxy to
#    another, this is usually why.
pvecm updatecerts

Removing a node

The order is the whole procedure. Three of these steps are hard to undo.

Cluster-wide riskremoving a node, in order
set -euo pipefail
DOOMED=pve-04

# --- On the node being removed ---
# 1. Move every guest off it. Nothing should be left running or configured.
#    Migrate what can migrate; back up and rebuild what cannot.
qm list
pct list

# 2. Take it out of HA management, so the CRM stops trying to place work on it.
ha-manager status

# --- On a node that is staying ---
# 3. If a QDevice is configured, remove it FIRST. Removing a node with a
#    QDevice in place is a documented way to end up unpicking state by hand.
pvecm status | grep -i qdevice && pvecm qdevice remove || true

# 4. Confirm the cluster is quorate and healthy before changing membership.
pvecm status

# 5. POWER THE DOOMED NODE OFF, and leave it off.
#    It must not be running when it is removed.

# 6. Remove it. This is the irreversible step.
pvecm delnode "$DOOMED"

# 7. Verify: expected votes have dropped, the node is gone from the nodelist.
pvecm status
pvecm nodes

# 8. Its directory under /etc/pve may remain. Check it is empty of guest
#    configurations BEFORE removing it - see the warning below.
ls -la "/etc/pve/nodes/$DOOMED/"

Replacing a failed node

The sequence for a node that has died and is not returning:

  1. Confirm it is genuinely dead, through out-of-band management. A node that is partitioned rather than dead is a different problem entirely.
  2. Move its guest configurations to a surviving node, for every guest on shared storage. Do this first.
  3. Recover the rest from backup. Anything that was on local storage.
  4. Remove it: pvecm delnode pve-04 from a surviving node.
  5. Tidy /etc/pve/nodes/pve-04/ once you have confirmed it holds nothing you need.
  6. Build the replacement. A fresh install. The hostname may be reused or not — Proxmox supports either, and reusing it keeps documentation simpler at the cost of some ambiguity in old logs.
  7. Join it as a new node, and verify as above.

Common failure modes

The join fails

Read-only / Safethe checks that resolve most failed joins
# pvecm add 192.0.2.11 --fingerprint AA:BB:...:99
detected the following error(s):
* this host already contains virtual guests
Check if node may join a cluster failed!

Illustrative output

SymptomCauseFix
this host already contains virtual guestsThe joining node is not emptyBack up and remove its guests, or rebuild it
Authentication failureWrong root password, or the fingerprint does not matchRe-copy the join information from the cluster
cluster not ready - no quorum?The cluster you are joining is not quorateFix the cluster first
Version mismatch errorsDifferent PVE versionsBring both to the same version
The join succeeds and the node shows only link 0--link1 was omittedAdd the address to corosync.conf and reload
Time-related failuresClock skewFix NTP on both, then retry

After a join, quorum does not settle

Check that the addition actually took on every node — pvecm status on each should show the same Config Version and the same expected votes. A node that has not picked up the new corosync.conf will disagree, and the disagreement is visible in that one line.

A break/fix exercise

Break/Fixintermediate25 mincluster

A node joins successfully and the cluster silently loses its link redundancy

Symptoms

  • pvecm add on the new node completed without error
  • pvecm status shows the node as a member with the expected vote count
  • The GUI shows all four nodes green and guests can be migrated to the new node
  • corosync-cfgtool -n on an existing node shows the new node reachable on LINK 0 only

Available evidence

  • The three original nodes each have ring0_addr and ring1_addr in corosync.conf
  • The new node entry in the nodelist has ring0_addr only
  • The join was performed with pvecm add and the peer IP, with no --link options
  • The second corosync VLAN is trunked to the new node switch port and the interface is up
Show diagnosis & remediation

Root cause

pvecm add joins with the links it is given. With no --link options, the joining node received a single link derived from the address used to reach the peer, so its nodelist entry has ring0_addr and nothing else. Nothing about this produces an error, because a one-link node in a multi-link cluster is a valid configuration - it simply has no redundancy.

Safe remediation

Add the missing ring1_addr to the new node entry in /etc/pve/corosync.conf, increase config_version, and apply with corosync-cfgtool -R. Then confirm from an existing node that corosync-cfgtool -n reports the new node reachable on both LINK 0 and LINK 1. Prefer joining with explicit --link0 and --link1 arguments so this does not arise.

Verification

corosync-cfgtool -n on every node shows the new node connected on both links. Administratively downing the primary link interface on the new node in a maintenance window leaves it a member of the cluster, reachable over link 1, with pvecm status still quorate.

Prevention

Make explicit --link arguments part of the join procedure on any multi-link cluster, and add a post-join verification step that runs corosync-cfgtool -n from an existing node rather than from the one just joined. A node that joined with fewer links than the cluster uses produces no symptom at all until the day link 0 fails, which is precisely when the redundancy was supposed to matter.

Production considerations

Common mistakes

  • Running pvecm add on the wrong node. It runs on the joiner.
  • Joining a node that has guests on it, and losing its configuration.
  • Joining without --link arguments on a multi-link cluster, silently ending up with one link.
  • Running delnode before moving the guest configurations off.
  • Removing a node that is partitioned rather than dead.
  • Powering a removed node back on without reinstalling it.
  • Removing a node while a QDevice is still configured.
  • Joining into a cluster that is already degraded.

Key takeaways

  • pvecm add runs on the joining node; pvecm delnode runs on a node that is staying. pvecm addnode is internal.
  • A joining node adopts the cluster’s configuration and loses its own. Join empty nodes.
  • On a multi-link cluster, join with explicit --link arguments and verify with corosync-cfgtool -n.
  • Move guest configurations off a dead node before delnode, and be certain the node is off first.
  • Removal is one-way. A removed node must be reinstalled before it can rejoin.
  • Removing a genuinely dead node promptly lowers the quorum threshold and restores failure tolerance.

Knowledge check

Knowledge check · 5 questions

  1. Q1. An administrator wants to add a new node to an existing three-node cluster. Where is pvecm add executed?

  2. Q2. Which of these must happen before running pvecm delnode on a node being decommissioned? Select all that apply.

  3. Q3. A node removed from a cluster can be powered back on later and rejoined without being reinstalled.

  4. Q4. A standalone Proxmox host running eight production guests needs to become part of an existing cluster. What is the supported approach?

  5. Q5. A four-node cluster loses one node permanently. Why does removing it promptly with delnode improve resilience rather than merely tidying up?

Passing score: 75%. Answers are checked in this browser.