Skip to main content
RunBook Academy

Proxmox VEXI · ClusteringCluster internals

Corosync and pmxcfs

Advanced⏱ ~24 minpvecmcorosync-cfgtool

What you'll learn

  • Explain how Corosync establishes membership and quorum, and what it does not do
  • Describe how pmxcfs replicates configuration and what its limits are
  • Read the status of each subsystem, including the virtual status files in /etc/pve
  • Attribute a cluster symptom to the correct layer instead of guessing

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

A Proxmox cluster is two independent pieces of machinery stacked on top of each other, and when something goes wrong the first useful question is which one broke.

  • Corosync answers who is in the cluster right now, and do we have a majority.
  • pmxcfs answers what does the cluster believe, and may I change it.

pmxcfs depends on corosync; corosync knows nothing about pmxcfs. That direction of dependency is the diagnostic shortcut: a corosync problem always produces pmxcfs symptoms, but a pmxcfs problem does not affect corosync. If pvecm status is healthy and /etc/pve is misbehaving, you are looking at the upper layer. If pvecm status is unhealthy, nothing you do to the upper layer will help.

Corosync — membership, quorum and messaging

Corosync provides three things and no more:

  • Cluster membership. Which nodes are currently reachable and participating.
  • Quorum. Whether the current membership constitutes a majority, computed by the corosync_votequorum provider.
  • Ordered messaging. A guarantee that a message sent to the group is delivered to every member in the same order.

That third one is easy to skip past and it is the reason the whole design works. pmxcfs does not implement replication; it implements applying changes in the order corosync delivers them, which is a much smaller problem.

Since Proxmox VE 6.0, corosync uses Kronosnet (knet) as its transport, over UDP unicast. Multicast is no longer required — a detail worth knowing because a great deal of older Proxmox material online assumes multicast and tells you to configure switches accordingly.

Network requirements

RequirementDetailWhy
Low latencyThe documentation calls for under 5 ms, i.e. LAN performanceCorosync is sensitive to latency jitter; beyond three nodes it degrades quickly
UDP 5405–5412 open between all nodesCorosync cluster trafficMembership and messaging. One port per link, starting at 5405
TCP 22 open between all nodessshd, used for cluster actionspvecm add, migration, and several pvecm operations use SSH
TCP 8006Web interface and APINode-to-node API calls, and the GUI proxying between nodes
A lightly loaded networkNot shared with backup, migration or storageBandwidth is negligible; contention is the problem
Read-only / Safewhat a healthy cluster looks like from corosync's point of view
# pvecm status
Cluster information
-------------------
Name:             prod-cluster
Config Version:   7
Transport:        knet
Secure auth:      on

Quorum information
------------------
Date:             Wed Aug 12 11:04:38 2026
Quorum provider:  corosync_votequorum
Nodes:            3
Node ID:          0x00000001
Ring ID:          1.1a4
Quorate:          Yes

Votequorum information
----------------------
Expected votes:   3
Highest expected: 3
Total votes:      3
Quorum:           2
Flags:            Quorate

Membership information
----------------------
  Nodeid      Votes Name
0x00000001          1 10.0.0.11 (local)
0x00000002          1 10.0.0.12
0x00000003          1 10.0.0.13

Illustrative output

Read-only / Safecorosync's own diagnostics, below the pvecm wrapper
set -euo pipefail

# Per-link status for this node. On knet this includes extended statistics.
corosync-cfgtool -s

# Every node with the status of each of its links. This is the command that
# shows you a second link that is configured and not actually working.
corosync-cfgtool -n

# The runtime configuration map, including live counters.
corosync-cmapctl | grep -E 'members|totem\.|runtime\.votequorum'

# What corosync itself said when the membership last changed.
journalctl -u corosync --since '1 hour ago' --no-pager

pmxcfs — the cluster filesystem

pmxcfs is a FUSE filesystem mounted at /etc/pve. Every configuration file that describes the cluster lives there, and every one of them is the same on every node.

The documentation describes it as “a database-driven file system for storing configuration files, replicated in real time to all cluster nodes using corosync”, and each part of that sentence has an operational consequence:

  • Database-driven. The backing store is a SQLite database at /var/lib/pve-cluster/config.db, with required permissions 0600. What you see under /etc/pve is a view of that database, not files on a disk.
  • A copy resides in RAM, which imposes a hard ceiling: 128 MiB for the entire tree.
  • Replicated in real time using corosync, which means writes require quorum and reads do not.

It is not a normal filesystem

The POSIX compatibility limitations are documented and they surprise people who try to treat /etc/pve as an ordinary directory:

LimitationConsequence
No symbolic links can be createdScripts that expect to drop a symlink into /etc/pve fail
Non-empty directories cannot be renamedReorganising by mv does not work
File permissions cannot be changedchmod is a no-op; the permission model is fixed
O_EXCL creates are not atomicLock files implemented with O_EXCL are unreliable here
O_TRUNC creates are not atomicA FUSE restriction; write to a temporary name and rename

The virtual status files

pmxcfs exposes several read-only JSON files that are generated rather than stored. They are the correct way to inspect cluster state programmatically, and they are frequently the fastest answer to a question.

FileContains
/etc/pve/.versionFile versions, used to detect modifications
/etc/pve/.membersInformation about cluster members
/etc/pve/.vmlistEvery guest in the cluster and which node owns it
/etc/pve/.clusterlogThe last 50 cluster log entries
/etc/pve/.rrdThe most recent RRD entries
Read-only / Safewhich node owns which guest, without asking every node
# cat /etc/pve/.vmlist
{
"version": 41,
"ids": {
"100": { "node": "pve-01", "type": "qemu", "version": 12 },
"101": { "node": "pve-02", "type": "qemu", "version": 9 },
"200": { "node": "pve-03", "type": "lxc", "version": 4 }}
}

Illustrative output

Read-only / Safeinspect pmxcfs state and its backing store
set -euo pipefail

# Is the daemon healthy? pmxcfs runs as the pve-cluster service.
systemctl status pve-cluster --no-pager

# Membership as pmxcfs sees it, which should agree with corosync.
cat /etc/pve/.members

# File versions, for detecting what changed.
cat /etc/pve/.version

# Recent cluster log entries.
cat /etc/pve/.clusterlog

# The backing database. Size it against the 128 MiB ceiling.
ls -lh /var/lib/pve-cluster/config.db
du -sh /etc/pve

# Confirm the mount is what you think it is.
findmnt /etc/pve

What lives in pmxcfs

PathContent
/etc/pve/corosync.confCluster configuration — itself replicated
/etc/pve/datacenter.cfgDatacenter-wide options
/etc/pve/storage.cfgStorage definitions
/etc/pve/user.cfgUsers and groups
/etc/pve/acl.cfgAccess control lists
/etc/pve/notifications.cfgNotification targets and matchers
/etc/pve/status.cfgExternal metric servers
/etc/pve/ha/resources.cfgHA resources
/etc/pve/ha/rules.cfgHA affinity rules
/etc/pve/sdn/SDN configuration
/etc/pve/ceph.confCeph configuration
/etc/pve/nodes/<name>/qemu-server/<vmid>.confVM configuration
/etc/pve/nodes/<name>/lxc/<vmid>.confContainer configuration
/etc/pve/priv/Cluster-shared secrets — replicated, root-only
/etc/pve/nodes/<name>/priv/Node-specific secrets — replicated, root-only
SymlinkPoints to
/etc/pve/localnodes/<local hostname>
/etc/pve/qemu-servernodes/<local hostname>/qemu-server/
/etc/pve/lxcnodes/<local hostname>/lxc/

Attributing a symptom to a layer

SymptomLayerFirst command
/etc/pve is read-onlyCorosync — quorum lostpvecm status
A node shows grey in the GUI, others are fineCorosync — membershipcorosync-cfgtool -n
Nodes evict each other intermittentlyCorosync — latency or linkjournalctl -u corosync
/etc/pve writes fail while quorum is finepmxcfs — daemon or spacesystemctl status pve-cluster, du -sh /etc/pve
Configuration differs between nodespmxcfs — replication stalledcat /etc/pve/.version on each node
The GUI is unreachable but the node is upNeither — pveproxysystemctl status pveproxy
Guests are running but nothing can be changedCorosync — quorum lostpvecm status

Configuration: corosync.conf

corosync.conf lives at /etc/pve/corosync.conf, which means it is inside the filesystem whose availability depends on it. That circularity is the single most important thing to understand about editing it, and it has its own lesson — the deep dive at the end of this part covers the editing procedure, multi-link configuration and the totem parameters in detail.

Two facts to carry forward from here:

  • config_version must be increased on every edit. Corosync uses it to decide whether a configuration it has been handed is newer than the one it is running. An edit without a version bump is ignored.
  • There is a local copy at /etc/corosync/corosync.conf. Corosync reads that one at start-up; the copy in /etc/pve is the cluster’s master and is distributed to the local copies. If corosync will not start, the local file is what you edit, because /etc/pve will not be available.

Production considerations

Common mistakes

  • Editing corosync.conf without increasing config_version. The edit is silently ignored.
  • Restarting corosync everywhere at once, risking a fence storm.
  • Assuming multicast is required. Corosync has used knet over unicast since PVE 6.0.
  • Blocking UDP 5405–5412 or TCP 22 between nodes with a host firewall.
  • Using /etc/pve as a replicated file share, against a 128 MiB cluster-wide ceiling.
  • Believing priv/ is node-local. It is replicated like everything else.
  • Moving a guest configuration off a node that is partitioned rather than dead.
  • Configuring a second corosync link and never testing failover.

Key takeaways

  • Corosync provides membership, quorum and ordered messaging; pmxcfs uses that ordering to keep /etc/pve identical everywhere.
  • The dependency runs one way, which makes pvecm status the first command for almost every cluster symptom.
  • Corosync uses knet over UDP unicast on ports 5405–5412, plus TCP 22 for cluster actions. It needs low latency, not bandwidth.
  • pmxcfs is a FUSE view of a SQLite database, capped at 128 MiB for the whole cluster, with real POSIX limitations.
  • The virtual files .version, .members, .vmlist, .clusterlog and .rrd are the supported way to inspect state.
  • Everything under /etc/pve is replicated, including priv/, and the directory a guest configuration sits in is its ownership record.

Knowledge check

Knowledge check · 5 questions

  1. Q1. Which ports must be reachable between nodes for corosync cluster traffic on a current Proxmox VE release?

  2. Q2. Which statements about /etc/pve are correct? Select all that apply.

  3. Q3. A pmxcfs fault can cause corosync to lose quorum.

  4. Q4. You need to know which node currently owns VM 100, from a node whose GUI is unavailable. What is the fastest reliable source?

  5. Q5. A configuration change to corosync.conf needs to take effect across a running three-node cluster. What is the safest way to apply it?

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