Proxmox VEXI · ClusteringCluster internals
Cluster networking deep dive: Corosync links, totem, and tuning
What you'll learn
- Read and modify /etc/pve/corosync.conf safely, including the recovery path when it is wrong
- Configure a second corosync link and prove it carries traffic
- Name the totem parameters that exist, their real defaults, and when changing them is justified
- Diagnose corosync problems with corosync-cfgtool, corosync-cmapctl and the journal
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
Corosync is the membership and messaging layer that holds a PVE cluster
together. When it has problems, every cluster operation stalls: migrations
freeze, the GUI hangs, /etc/pve goes read-only, HA decisions do not fire.
A large proportion of Proxmox cluster outages trace back to it, and almost
all of those are a network problem wearing a corosync costume.
This lesson is the operational anatomy: the configuration file, the editing procedure, multi-link redundancy, the totem parameters that actually exist, and diagnosis.
What corosync does, and what it does not
Corosync runs on every node and provides:
- Membership — which nodes are in the cluster. A join or a departure is propagated within a token rotation.
- Quorum — the majority-vote algorithm, implemented by
corosync_votequorum.pvecm statusreads its state from here. - Ordered messaging — reliable delivery to a closed process group, in the same order on every member.
It does not replicate configuration, manage services, or decide where guests
run. pve-cluster (pmxcfs) subscribes to corosync for membership and uses
the ordered messaging to keep /etc/pve identical everywhere; pve-ha-crm
and pve-ha-lrm make the HA decisions. Proxmox does not use Pacemaker, so
Pacemaker tools such as crm_mon and pcs are not part of this stack and
will not be present.
The configuration file
/etc/pve/corosync.conf is the cluster’s master copy, and because it lives
in pmxcfs it is replicated automatically. Each node also keeps a local copy
at /etc/corosync/corosync.conf, which is what corosync actually reads at
start-up.
That pair is the single most important structural fact in this lesson, and the recovery section at the end depends on it.
A real PVE 9 three-node configuration:
logging {
debug: off
to_syslog: yes
}
nodelist {
node {
name: pve-01
nodeid: 1
quorum_votes: 1
ring0_addr: 10.0.0.11
}
node {
name: pve-02
nodeid: 2
quorum_votes: 1
ring0_addr: 10.0.0.12
}
node {
name: pve-03
nodeid: 3
quorum_votes: 1
ring0_addr: 10.0.0.13
}
}
quorum {
provider: corosync_votequorum
}
totem {
cluster_name: prod-cluster
config_version: 3
interface {
linknumber: 0
}
ip_version: ipv4-6
link_mode: passive
secauth: on
version: 2
}
Four things to notice, because older documentation shows something quite different:
- The
interfaceblock carries onlylinknumber. Addresses are per node innodelist, asring0_addr. There is nobindnetaddr, nomcastaddrand nomcastport— those belong to the pre-knet era. - There is no
transportline. knet is the default in corosync 3 and PVE does not override it.pvecm statusconfirms it withTransport: knet. secauth: on. Corosync traffic is authenticated and encrypted by default on a PVE cluster;pvecm statusshowsSecure auth: on. There is nothing to turn on here.- No totem timers are set. PVE ships the corosync defaults, which are correct for a LAN cluster. That is a deliberate choice, not an omission.
Editing it without losing the cluster
set -euo pipefail
# 1. Back up both copies. The local one is your recovery path.
cp /etc/pve/corosync.conf /root/corosync.conf.bak
cp /etc/corosync/corosync.conf /root/corosync.conf.local.bak
# 2. Edit the copy in /etc/pve - it is the one that replicates.
# Increase config_version by exactly one as part of the same edit.
nano /etc/pve/corosync.conf
# 3. Verify it parses before it is applied anywhere.
corosync-cfgtool -s >/dev/null && echo 'current instance still healthy'
grep -E 'config_version|linknumber|ring[0-9]_addr' /etc/pve/corosync.conf
# 4. Ask every corosync instance to reload, without restarting anything.
corosync-cfgtool -R
# 5. Confirm the new version is live on every node.
pvecm status | grep -E 'Config Version|Quorate|Total votes'
corosync-cfgtool -nAdding a second link
A single corosync link is a single point of failure. If it drops for longer than the token timeout the node leaves the membership, and with HA armed it fences itself.
On PVE 9 a second link is two additions: an interface block with
linknumber: 1, and a ring1_addr on every node.
nodelist {
node {
name: pve-01
nodeid: 1
quorum_votes: 1
ring0_addr: 10.0.0.11
ring1_addr: 10.0.1.11
}
node {
name: pve-02
nodeid: 2
quorum_votes: 1
ring0_addr: 10.0.0.12
ring1_addr: 10.0.1.12
}
node {
name: pve-03
nodeid: 3
quorum_votes: 1
ring0_addr: 10.0.0.13
ring1_addr: 10.0.1.13
}
}
totem {
cluster_name: prod-cluster
config_version: 4
interface {
linknumber: 0
}
interface {
linknumber: 1
}
ip_version: ipv4-6
link_mode: passive
secauth: on
version: 2
}
Requirements that are not optional:
- Every node must have every link. A nodelist where one node lacks
ring1_addris not a valid two-link cluster. - The links must be genuinely independent. Two VLANs on the same physical switch survive a VLAN misconfiguration and not a switch failure, which is the failure you were buying protection against.
- Each link needs its own UDP port, allocated from the 5405–5412 range. A firewall permitting 5405 and not 5406 produces a link that is configured and dead.
link_mode controls how the links are used. passive — the PVE default —
sends over the highest-priority working link and fails over. active sends
over all links simultaneously, which lowers latency and raises load. For
almost every cluster, passive is right.
# corosync-cfgtool -nLocal node ID 1, transport knet
nodeid: 2 reachable
LINK: 0 udp (10.0.0.11->10.0.0.12) enabled connected mtu: 1397
LINK: 1 udp (10.0.1.11->10.0.1.12) enabled connected mtu: 1397
nodeid: 3 reachable
LINK: 0 udp (10.0.0.11->10.0.0.13) enabled connected mtu: 1397
LINK: 1 udp (10.0.1.11->10.0.1.13) enabled connected mtu: 1397Illustrative output
Totem parameters: what exists, and the real defaults
Most clusters need none of these. They are here so that you can recognise them, and so that you do not copy a WAN-tuning snippet into a LAN cluster because it appeared authoritative on a forum.
| Parameter | Default | What it does |
|---|---|---|
token | 3000 ms | Time without receiving the token before token loss is declared |
token_coefficient | 650 ms | Added to the effective token timeout per node beyond two, when the nodelist has three or more nodes |
token_retransmits_before_loss_const | 4 | Retransmission attempts before a new configuration is formed |
consensus | 3600 ms | Time to wait for consensus on membership before starting a new round |
max_messages | 17 | Maximum messages a member may send after receiving the token |
knet_ping_interval | derived from the token timeout | knet link liveness probing interval |
link_mode | passive | How multiple links are used: passive, active or rr |
ip_version | ipv4-6 on PVE | Address family preference |
Diagnosis
Symptoms that point at corosync:
- Cluster operations stall — migrations, configuration changes, backups
- Nodes appear and disappear from the GUI with no hardware fault
/etc/pvegoes read-only intermittently- HA fires when nothing was wrong
set -euo pipefail
# The PVE summary: quorum, votes, membership, config version.
pvecm status
pvecm nodes
# This node's links, with knet extended statistics.
corosync-cfgtool -s
# Every node with per-link status. Use -i with -s to narrow to one address.
corosync-cfgtool -n
# Live runtime counters. Retransmits and token loss show up here.
corosync-cmapctl -m stats | grep -Ei 'retrans|token|rx_error|tx_error' || true
corosync-cmapctl | grep -E 'runtime\.votequorum|members'
# What corosync and pmxcfs said as it happened.
journalctl -u corosync --since '2 hours ago' --no-pager
journalctl -u pve-cluster --since '2 hours ago' --no-pager| Log pattern | Cause | Fix |
|---|---|---|
Repeated A processor joined or left the membership | The link is dropping | Switch port, cable, NIC, MTU, or contention |
Token has not been received in N ms | Latency exceeds the effective token timeout | Fix the network first; tune only as a last resort |
Retransmit List: growing | Packet loss on the corosync path | Cable, switch, or a saturated shared link |
Configuration version mismatch | Nodes are running different corosync.conf | Increase config_version and reload; confirm all nodes agree |
Invalid configuration at start-up | The file does not parse | Repair the local copy at /etc/corosync/corosync.conf |
Common operational scenarios
Changing a node’s corosync address
# Edit /etc/pve/corosync.conf: change that node's ring0_addr, and increase
# config_version. Then reload cluster-wide rather than restarting.
corosync-cfgtool -R
pvecm status | grep -E 'Config Version|Quorate'
corosync-cfgtool -n
Update /etc/hosts and DNS to match. A node whose corosync address and
hostname resolution disagree will work until something resolves the name.
Removing a node
Do not edit the nodelist by hand. pvecm delnode removes the node from the
nodelist, increases config_version and adjusts expected votes as one
operation, and hand-editing gets one of those three wrong often enough to be
worth avoiding.
# From a node that is staying, with the cluster quorate.
pvecm delnode pve-03
pvecm status
The removed node must be reinstalled before it can rejoin — it still holds a
replica of the cluster’s secrets from /etc/pve/priv/, which is why the
documented guidance is to reinstall rather than repurpose.
Production considerations
- A dedicated network for corosync. Not shared with migration, backup or storage. Contention, not bandwidth, is what breaks it.
- Two links on independent hardware, tested by inducing a failure.
- Leave the totem defaults alone unless you have a measured reason.
- Encryption is already on.
secauth: onis the PVE default; there is no crypto to enable. - Do not make corosync changes during a rolling upgrade. One variable at a time.
- Sample the retransmit counters into monitoring so a marginal link is a trend rather than a surprise.
Common mistakes
- Editing without increasing
config_version, so the change is ignored. - Restarting corosync on every node at once.
- Copying a pre-knet configuration with
bindnetaddr,mcastaddrandtransport: udpufrom old documentation. - Reaching for Pacemaker tools. PVE does not use Pacemaker.
- Raising
tokento silence a flapping link, permanently slowing failure detection to mask a fixable fault. - Computing detection time from
tokenalone, ignoringtoken_coefficient. - Two links on the same physical switch.
- Hand-editing the nodelist to remove a node instead of using
pvecm delnode.
Key takeaways
corosync.confexists twice: the master in/etc/pveand the local copy in/etc/corosyncthat corosync actually reads. The second is your recovery path.- Increase
config_versionon every edit, then prefercorosync-cfgtool -Rover a restart. - PVE 9 uses knet over unicast; addresses live per node as
ringX_addrand theinterfaceblock carries onlylinknumber. secauth: onis the default — corosync traffic is already authenticated and encrypted.- Real defaults:
token3000 ms,token_coefficient650 ms per node beyond two,token_retransmits_before_loss_const4,consensus3600 ms. - A second link is unproven until you have downed the first one and watched the cluster stay together.
Knowledge check
Knowledge check · 5 questions
Q1. An engineer edits /etc/pve/corosync.conf to add a second link, saves it, and observes that nothing changes. The file content is correct on every node. What is the most likely cause?
Q2. Which of these appear in a current PVE 9 corosync.conf? Select all that apply.
Q3. On a five-node cluster running defaults, the effective corosync token timeout is longer than the 3000 ms token value, because token_coefficient adds time for each node beyond two.
Q4. Nodes are intermittently leaving the membership during the nightly backup window. Which response is correct?
Q5. Corosync fails to start on one node after a configuration change, and /etc/pve on that node is read-only. Where do you make the repair?
Passing score: 75%. Answers are checked in this browser.