Proxmox VEXII · High AvailabilityHA advanced
CRS and dynamic load balancing (PVE 9.2)
What you'll learn
- Distinguish the basic, static and dynamic CRS scheduler modes and what each measures
- Read and set the crs keys in datacenter.cfg, including the rebalance threshold, margin and hold duration
- Observe a rebalance decision and attribute a migration you did not order
- Constrain or exclude specific guests from automatic rebalancing
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
Every time the HA stack needs to decide where a guest should run, something makes that decision. That something is the Cluster Resource Scheduler, and until recently it was easy to ignore because its default answer was crude and its scope was narrow: recovery after a node failure, and nothing else.
PVE 9.2 changes both halves. There is a dynamic scheduler mode that reads
actual load, and there is an automatic rebalancer that can act on it while
nothing has failed at all. If you upgrade a cluster and later find migrations
in the task log that nobody ordered, this is the lesson that explains them.
The three modes
CRS is configured in /etc/pve/datacenter.cfg under a single crs key with
several sub-options. The scheduler mode is ha=:
| Mode | Decides on | Behaviour |
|---|---|---|
basic | Guest counts and HA rules only | The historical default. Spreads services numerically. |
static | Configured CPU and memory of each guest | Packs by what guests were allocated, not what they use. |
dynamic | Observed node load | Places by what the nodes are actually doing. |
The distinction between static and dynamic is the one that matters, and
the word “static” is a little misleading. static is not a fixed placement -
it is a scheduler whose inputs are static: the cores and memory numbers
in each guest’s config. It does not care whether a VM configured with 32 GiB
is using 2 GiB or 31 GiB.
dynamic uses observed load, so it converges on something closer to the truth
- and it also means the scheduler’s decisions change over time on a cluster whose configuration has not changed at all.
set -euo pipefail
grep -E '^crs:' /etc/pve/datacenter.cfg || echo 'crs not set - default ha=basic'
# The full parsed view, including defaults the file does not spell out.
pvesh get /cluster/options --output-format json-pretty | head -40set -euo pipefail
# Observe first. Do not enable rebalancing in the same change.
pvesh set /cluster/options --crs 'ha=dynamic'
grep -E '^crs:' /etc/pve/datacenter.cfgAutomatic rebalancing: the part that moves things
Setting ha=dynamic changes where new placements go. It does not, on its
own, move a running guest. That is a separate switch, and it is off by
default:
| Key | Default | What it controls |
|---|---|---|
ha-auto-rebalance | 0 | Whether the cluster rebalances running guests at all |
ha-auto-rebalance-threshold | 30 | Node imbalance percentage that triggers a rebalance |
ha-auto-rebalance-margin | 10 | Minimum improvement percentage required to commit a migration |
ha-auto-rebalance-hold-duration | 3 | HA rounds the condition must persist before acting |
ha-auto-rebalance-method | bruteforce | Scoring approach: bruteforce or topsis |
ha-rebalance-on-start | 0 | Use CRS when an HA service goes from stopped to started |
Read those four numeric keys as one sentence: the cluster acts when nodes differ by more than 30%, the move must improve things by at least 10%, and the condition must hold for 3 consecutive HA rounds first.
Three brakes, all adjustable, all in the direction of not thrashing.
set -euo pipefail
pvesh set /cluster/options --crs \
'ha=dynamic,ha-auto-rebalance=1,ha-auto-rebalance-threshold=40,ha-auto-rebalance-margin=15,ha-auto-rebalance-hold-duration=6'
grep -E '^crs:' /etc/pve/datacenter.cfgThose numbers are deliberately more conservative than the defaults - a wider imbalance tolerated, a larger improvement demanded, and twice as long for the condition to persist. That is the right starting posture for a first enablement: you want to see the mechanism act a few times before you let it act often.
Constraining the scheduler
Three levels of control, from narrowest to broadest.
Exclude one guest
Every HA resource has an auto-rebalance property, documented as “HA resource
may be migrated during automatic rebalancing”, defaulting to 1.
set -euo pipefail
VMID=101
ha-manager set "vm:$VMID" --auto-rebalance 0
ha-manager config --type vmReach for this on anything with a reason not to move: a latency-sensitive database whose replication would notice, a guest with device passthrough that is pinned to specific hardware, a licensing arrangement tied to a socket count.
Constrain where it may move things
Node-affinity rules bound the placement search for every scheduler mode. The scheduler optimises within the rules; it does not override them.
set -euo pipefail
ha-manager rules add node-affinity db-tier \
--resources 'vm:101,vm:102' \
--nodes 'pve-01:2,pve-02:1' \
--strict 1
ha-manager rules configNegative resource-affinity is the other useful shape here: two guests that must never share a node - a database primary and its replica, two halves of a quorum - stay separated no matter what the load numbers say.
Turn rebalancing off, keep dynamic placement
set -euo pipefail
pvesh set /cluster/options --crs 'ha=dynamic,ha-auto-rebalance=0'
grep -E '^crs:' /etc/pve/datacenter.cfgThis is the configuration most production clusters should sit at for their first months on 9.2: better recovery placement, no unattended migrations.
Observing a decision
set -euo pipefail
# Which node is the CRM master right now.
ha-manager status | grep -i master
# Its reasoning, live.
journalctl -u pve-ha-crm -f --no-pager
# Migrations that the HA stack initiated, rather than a user.
pvesh get /cluster/tasks --output-format json-pretty \
| grep -B2 -A4 'qmigrate' | head -40# journalctl -u pve-ha-crm --since '30 min ago' --no-pager | tail -8pve-ha-crm[2411]: node 'pve-01' load 0.81, 'pve-02' load 0.34, 'pve-03' load 0.39
pve-ha-crm[2411]: imbalance 47% exceeds threshold 40%, round 6 of hold 6
pve-ha-crm[2411]: crs: considering vm:104 pve-01 -> pve-02, predicted improvement 22%
pve-ha-crm[2411]: service 'vm:104': migrate to node 'pve-02'
pve-ha-crm[2411]: migrate service 'vm:104' to node 'pve-02' (running)
pve-ha-crm[2411]: service 'vm:104': state changed from 'started' to 'migrate'
pve-ha-crm[2411]: service 'vm:104': state changed from 'migrate' to 'started'Illustrative output
Every input to the decision is in those lines: the observed loads, the imbalance against the threshold, the hold-duration counter, and the predicted improvement against the margin. When a migration surprises you, this is the record that explains it - and when the scheduler is not doing something you expected, it is where you find which brake stopped it.
Rolling it out without an incident
A staged sequence, because every step here is observable and the temptation is to do them all at once.
- Record the current setting and put it in the change ticket.
grep crs: /etc/pve/datacenter.cfg, including the case where it is absent. - Move to
ha=staticfirst if you were onbasic. Placement now accounts for guest size, which is an improvement on counting, and nothing moves on its own. - Move to
ha=dynamic. Still nothing moves on its own. Watch recovery placement across a planned node reboot and confirm it lands sensibly. - Opt out the guests that must not move with
--auto-rebalance 0, and put node-affinity rules on the tiers that have placement constraints. Do this before the next step, not after. - Enable
ha-auto-rebalance=1with a raised threshold and hold duration. Watch the CRM log for a week. - Tighten towards the defaults only if the observed behaviour is boring.
The step people skip is 4, and it is the one that turns step 5 from a configuration change into an incident.
Knowledge check
Knowledge check · 5 questions
Q1. What is the practical difference between the static and dynamic CRS scheduler modes?
Q2. Which datacenter.cfg key stops a transient load spike - a nightly backup, say - from triggering a migration?
Q3. A production database VM should not be moved by the automatic rebalancer, but must still be recovered by HA if its node fails. Which approaches achieve that? Select all that apply.
Q4. On a cluster whose guests all sit on local storage, changing the CRS mode from basic to dynamic changes no placement outcome at all.
Q5. Which service’s journal records the CRS placement decisions, including the observed loads and the predicted improvement?
Passing score: 75%. Answers are checked in this browser.