Proxmox VE's HA Manager has always been able to fail a guest over when a node dies. What it couldn't do, for most of its life, was decide where guests should live in the first place based on how busy your nodes actually are. That gap closed with the Cluster Resource Scheduler (CRS), and Proxmox VE 9.2 pushed it a lot further by adding a genuine dynamic load-balancing mode that watches real-time CPU and memory usage and live-migrates HA-managed guests to smooth things out automatically.

This guide walks through what CRS actually does, the three scheduler modes it supports, how to turn on automatic rebalancing without surprising yourself at 3 a.m., and how it interacts with the node and resource affinity rules you may already have configured for HA.

Prerequisites

  • A Proxmox VE cluster with at least three nodes and working quorum (CRS only acts on HA-managed resources, and HA needs quorum to make decisions).
  • Proxmox VE 9.1 or later for the dynamic scheduling mode. The basic and static modes have existed for longer, but the automatic rebalancing controls described here are new enough that you should confirm your version with pveversion -v before following along.
  • At least a few guests already configured as HA resources. CRS has nothing to schedule if nothing is under HA management, so set up HA on your VMs and containers first if you haven't already.
  • Admin access to the Datacenter view in the web UI, or root/API-token access to edit /etc/pve/datacenter.cfg from the shell.

What the Cluster Resource Scheduler Actually Does

It helps to separate two things that people often lump together: HA decides whether a guest should be running and where it should be recovered to after a failure. CRS decides, among the valid candidate nodes, which one is the best one — both when a resource starts or fails over, and, if you enable it, continuously while the cluster is healthy.

Every time the Cluster Resource Manager (CRM) needs to place an HA-managed resource, it asks the configured scheduler to rank the eligible nodes. "Eligible" already accounts for your node and resource affinity rules — CRS never places a resource somewhere an affinity rule forbids. It only chooses among the nodes those rules allow.

Step 1: Understand the Three Scheduler Modes

The scheduler mode is set with the ha sub-option of the crs key in datacenter.cfg. There are three:

ModeWhat it looks atBehavior
basicNumber of HA services per nodeSpreads services evenly by count only. This was the original, pre-CRS-tuning behavior and is still the default.
staticConfigured CPU cores and memory of each guestPlaces resources based on their configured vCPU/RAM footprint, not what they're actually using. Good middle ground if you don't want live metrics driving migrations.
dynamicConfigured CPU/memory and real-time utilizationFactors in actual average CPU and memory usage of running guests alongside their quotas. This is what makes true load-based rebalancing possible.

If you only want smarter initial and failover placement without any ongoing rebalancing, static is usually enough. If you want the cluster to actively correct imbalance while everything is healthy — say, after you've been doing ad-hoc manual migrations for a week and node loads have drifted — you need dynamic.

Step 2: Set the Scheduler Mode

Via the GUI: go to Datacenter → Options → Cluster Resource Scheduling and pick the HA Scheduling Mode from the dropdown (Basic / Static / Dynamic).

Via the shell, edit /etc/pve/datacenter.cfg (this file lives on the shared pmxcfs, so the change propagates to every node immediately):

# set the dynamic scheduler
crs: ha=dynamic

You can also append the other crs sub-options on the same line, comma-separated — the full key looks like this:

crs: ha=dynamic,ha-rebalance-on-start=1,ha-auto-rebalance=1,ha-auto-rebalance-threshold=30,ha-auto-rebalance-margin=10,ha-auto-rebalance-hold-duration=3,ha-auto-rebalance-method=bruteforce

There's no need to restart any service after editing the file — the CRM picks up datacenter.cfg changes on its own within its normal evaluation cycle.

Step 3: Decide Whether You Want Rebalancing on Start

ha-rebalance-on-start (0 or 1, default 0) controls a narrower behavior than full auto-rebalancing: when an HA resource is started or recovered, should CRS pick the single least-loaded eligible node at that moment, or just take whatever node it would have picked without this option?

crs: ha=static,ha-rebalance-on-start=1

This is a low-risk setting to turn on even if you're not ready for continuous auto-rebalancing — it only affects the moment a guest starts or fails over, not guests that are already running happily on a node.

Step 4: Turn On Automatic Rebalancing (the Real Load Balancer)

This is the part that live-migrates running guests without you touching anything. It's controlled by ha-auto-rebalance (0 or 1, default 0) and only takes effect when the scheduler mode is static or dynamicbasic mode has nothing meaningful to rebalance against.

crs: ha=dynamic,ha-auto-rebalance=1

With just that, CRS will periodically check whether the cluster is imbalanced and, if so, live-migrate one or more HA resources to level things out. The behavior is tuned with four more sub-options:

  • ha-auto-rebalance-threshold (0–100, default 30) — how imbalanced the cluster has to be, in percent, before CRS will act at all. Lower values make CRS more eager to rebalance; higher values mean it only steps in for serious skew.
  • ha-auto-rebalance-margin (0–100, default 10) — the minimum relative improvement a candidate migration has to deliver before CRS commits to it. This stops the scheduler from shuffling guests around for a marginal, not-worth-the-disruption gain.
  • ha-auto-rebalance-hold-duration (default 3) — how long, in evaluation cycles, CRS waits after one rebalancing action before it's allowed to trigger another. This is what keeps a noisy cluster from thrashing guests back and forth.
  • ha-auto-rebalance-method (bruteforce or topsis, default bruteforce) — the algorithm used to score candidate placements. bruteforce exhaustively evaluates placement options and is the safer, better-tested default. topsis is a multi-criteria decision method available as an alternative; stick with the default unless you have a specific reason and have tested it in a non-production cluster first.

A reasonably conservative starting point for a production cluster looks like this:

crs: ha=dynamic,ha-auto-rebalance=1,ha-auto-rebalance-threshold=35,ha-auto-rebalance-margin=15,ha-auto-rebalance-hold-duration=5

That raises the threshold a bit above the default and adds breathing room via a larger margin and hold duration, so CRS only moves guests for genuinely worthwhile, sustained imbalance rather than reacting to a short-lived CPU spike.

A Worked Example

It's easier to reason about the four rebalancing sub-options with actual numbers behind them. Say you're running a three-node cluster where each node has HA-managed guests, and node pve1 is sitting at roughly 70% average CPU utilization while pve2 and pve3 are around 40%.

With ha-auto-rebalance-threshold=30, CRS calculates the cluster's imbalance as the spread between the busiest and least-loaded eligible node — here, a 30-point gap, which just clears the default threshold. It then looks for a candidate migration: moving one HA-managed guest off pve1 onto pve2 or pve3.

Before committing, CRS checks that gain against ha-auto-rebalance-margin. If moving a specific guest would only narrow the gap by 6 points, and your margin is set to 10, CRS skips that particular move and looks for a better one — or does nothing at all this cycle if no single migration clears the margin. This is what stops the scheduler from relocating a guest for a barely-there improvement.

Once a migration does go ahead, ha-auto-rebalance-hold-duration puts the brakes on for the next several evaluation cycles, so CRS doesn't immediately try to "fix" the cluster again before the effects of the first migration have even settled — and before the guest's workload has had a chance to normalize on its new node.

The practical upshot: raising the threshold makes CRS tolerate more day-to-day imbalance before it acts; raising the margin makes it pickier about which migrations are worth doing; and raising the hold duration slows down how often it's willing to act at all. Tune whichever one matches the specific behavior you're trying to avoid.

CRS and Node Maintenance Mode

When you put a node into maintenance mode (ha-manager crm-command node-maintenance enable <node>), CRS excludes that node from placement decisions entirely — it won't be considered a valid target for new placements, failovers, or rebalancing migrations until maintenance mode is disabled again. This means you can safely drain a node for patching without also having to disable auto-rebalancing first; CRS simply routes around it. Once you take the node out of maintenance mode, it becomes eligible again on the next evaluation cycle, and if it's now the least-loaded node in the cluster, don't be surprised if CRS starts migrating guests back onto it fairly quickly if auto-rebalance is enabled.

Troubleshooting: "CRS Doesn't Seem to Be Doing Anything"

If you've enabled dynamic mode and auto-rebalance but aren't seeing any migrations, work through this checklist before assuming something is broken:

  • Confirm the setting actually saved. Run cat /etc/pve/datacenter.cfg and check the crs: line reads back exactly what you set — a typo in a sub-option name is silently ignored rather than rejected in some versions.
  • Check that the resources you expect to move are actually HA-managed. ha-manager config lists everything under HA; if a guest isn't there, CRS can't touch it no matter how imbalanced the cluster is.
  • Verify the cluster is actually imbalanced past your threshold. A quiet homelab cluster with everything sitting near idle may genuinely never cross a 30% imbalance threshold, which means there's nothing for CRS to correct — that's working as intended, not a bug.
  • Check for restrictive affinity rules. If every resource is pinned via strict node affinity to the node it's already on, CRS has no legal alternative placement to move it to.
  • Give it time. Between the evaluation cycle itself and the hold-duration cooldown after any prior migration, it can take longer than you'd expect for the first rebalancing action to appear, especially right after you've changed the configuration.

Step 5: Respect Your Affinity Rules

CRS does not override the placement constraints you've already defined for HA. If you've set up node affinity rules restricting a resource to a subset of nodes, or resource affinity rules that keep two guests together (positive affinity) or apart (negative affinity), CRS treats those as hard boundaries and only optimizes placement within them.

Check your current rules before enabling auto-rebalance, since a cluster with tight affinity constraints may have very little room to actually rebalance into:

cat /etc/pve/ha/rules.cfg

If most of your HA resources are pinned to one or two nodes via strict node affinity, auto-rebalancing will have almost nothing to do — which is fine, but worth knowing before you assume CRS isn't working.

Step 6: Watch It Work

There's no dedicated "CRS log" separate from the general HA log, so keep an eye on the usual places:

# tail the HA manager's cluster-wide log
tail -f /var/log/pve/tasks/index

# or watch overall HA status live
watch -n 5 ha-manager status --verbose

In the web UI, rebalancing migrations show up the same way any other live migration does, in the cluster's task log, and the resource keeps its started state throughout — there's no downtime, just a live migration triggered by the scheduler instead of by a human.

If you want to sanity-check imbalance without waiting for CRS to act on its own, compare per-node CPU and memory usage in Datacenter → Summary or with:

pvesh get /nodes --output-format json | python3 -m json.tool

Common Mistakes

  • Enabling ha-auto-rebalance in basic mode and expecting load balancing. Basic mode only counts services, so "rebalancing" under it just evens out the number of guests per node — it has no concept of actual load. Use static or dynamic if load is what you care about.
  • Leaving the threshold and margin at aggressive values on a cluster with bursty workloads. A low threshold combined with a short hold duration can turn temporary CPU spikes into unnecessary live migrations, which themselves consume CPU and network bandwidth on the source and target nodes.
  • Forgetting that CRS only manages HA resources. Guests that aren't under HA management are invisible to the scheduler — they're neither counted in the imbalance calculation as movable resources nor migrated by it. Turn on HA for a guest first if you want CRS to consider it.
  • Assuming dynamic mode requires no configured CPU/memory limits. Dynamic mode blends real-time usage with the guest's configured quotas — if every guest is set to use "all available" resources with no sensible limits, the scheduler has less useful signal to work with.
  • Testing auto-rebalance for the first time in production during business hours. Even though live migration is non-disruptive to the guest's uptime, it does briefly increase load on both the source and destination node and consumes migration network bandwidth. Validate on a maintenance window first.

Best Practices

  • Start with static mode and ha-rebalance-on-start=1 before jumping straight to full dynamic auto-rebalancing. It gets you smarter placement on start/failover with zero risk of surprise migrations while the cluster is running.
  • When you do move to dynamic with ha-auto-rebalance=1, raise the default threshold and margin slightly for production clusters, and keep the hold duration long enough to avoid thrashing.
  • Make sure your migration network has enough headroom — CRS-triggered migrations compete with any manual migrations, backup jobs, and replication traffic you already have scheduled.
  • Pair CRS with sensible node and resource affinity rules rather than relying on it to figure everything out from scratch. CRS optimizes placement; it doesn't replace the judgment calls you make about which guests should never share a node.
  • Revisit your crs settings after adding or removing nodes — a threshold and margin tuned for a five-node cluster may behave very differently once you're running eight or three.
  • Keep bruteforce as your rebalancing method unless you've specifically evaluated topsis against your workload pattern in a test cluster.

FAQ

Does CRS migrate non-HA guests?

No. CRS only places and rebalances resources that are under HA management. Regular VMs and containers outside of HA are never touched by the automatic rebalancer.

What's the difference between CRS and manually load-balancing with live migration?

Functionally they end up in the same place — a guest running on a different, less-loaded node — but CRS does it continuously and automatically based on the thresholds you set, without anyone needing to notice the imbalance and act on it manually.

Will enabling dynamic mode cause constant migrations?

Not if it's tuned sensibly. The threshold, margin, and hold-duration settings exist specifically to prevent that: a migration only happens when imbalance exceeds the threshold, the improvement clears the margin, and enough cycles have passed since the last rebalancing action.

Can I use CRS without HA at all?

No — CRS is part of the HA Manager and only schedules HA-managed resources. If you're not using HA, CRS has nothing to do.

Does changing the scheduler mode affect already-running guests immediately?

No. Changing ha, or turning on auto-rebalance, doesn't itself trigger any migrations. Placement decisions for already-running guests only happen on the next start, recovery, or the next scheduled rebalancing evaluation, whichever applies.

Is topsis better than bruteforce?

They're different scoring approaches rather than one being strictly better. bruteforce is the long-standing default and the more thoroughly tested path; topsis is a newer multi-criteria alternative worth evaluating only if you have a specific placement pattern that bruteforce handles poorly.

Does CRS take backup jobs or replication traffic into account?

No. CRS's load calculations are based on CPU and memory usage (and, in static mode, configured quotas) — it has no visibility into backup schedules, replication jobs, or storage I/O. If you run backups during a window where CRS might also decide to rebalance, both will compete for the same migration and storage network, so it's worth staggering them deliberately rather than assuming the scheduler will account for it.

Can I disable CRS for specific resources while leaving it on for everyone else?

Not directly — the scheduler mode and auto-rebalance settings are cluster-wide in datacenter.cfg, not per-resource. What you can do is use node or resource affinity rules to constrain a specific guest so tightly (a single-node node affinity rule, for example) that CRS has no alternative placement to move it to, which achieves the same practical effect for that one resource.

How do I revert to the old behavior if auto-rebalance causes problems?

Set ha-auto-rebalance=0 in the crs line, or drop back to ha=basic entirely to disable CRS-based scheduling altogether. Neither change affects guests that are already running — it only changes how future placement and rebalancing decisions are made.

Conclusion

CRS turns HA placement from "wherever the scheduler last happened to put it" into something you can actually tune. Start conservatively — static mode with rebalance-on-start is a safe first step — and only turn on full automatic rebalancing once you understand how the threshold, margin, and hold-duration settings interact with your cluster's actual workload patterns. Done carefully, it removes one more class of manual cluster babysitting without introducing surprise migrations during business hours.