Dynamic Resource Scheduling
When you create a VM, you shouldn't have to think about which physical host runs it. Manual placement doesn't scale. It leads to uneven resource distribution, overloaded nodes, and idle capacity sitting right next to them. The scheduler makes this decision for you.
This is what VMware calls DRS (Distributed Resource Scheduler) and what OpenStack Nova handles with its filter-and-weight scheduler. flex.plane takes a simpler, stateless approach.
How scheduling works
When a VM is created within a zone, the scheduler decides which host runs it. The process is straightforward:
- Gather candidates. The scheduler queries all hosts in the target zone and filters out nodes that are offline.
- Check resources. Nodes that don't have enough available memory for the requested compute profile are eliminated.
- Pick the best fit. From the remaining candidates, the scheduler selects the node with the most available memory.
If you explicitly specify a host when creating a VM, the scheduler is bypassed entirely and the VM is placed on the host you chose (assuming it has sufficient resources).
mutation {
createVM(
zone: "eu-central"
# omit `host` to let the scheduler decide
name: "web-server"
options: {
image: "ubuntu-2404"
computeProfile: "medium-profile-id"
storageProfile: "standard-profile-id"
sshPublicKey: "ssh-ed25519 AAAA..."
interfaces: [{ network: "100" }]
user: "admin"
password: "initial-password"
backup: false
}
) {
id
name
host { id name }
}
}
The current strategy is least-loaded memory. Memory is the hard constraint in virtualization. You can overcommit CPU (a VM using 10% of its allocated cores still shares the physical CPU), but you can't overcommit memory without risking OOM kills. Placing VMs where memory headroom is largest keeps your Proxmox cluster balanced and avoids pressure on any single node.
Stateless by design
Like everything else in flex.plane, the scheduler has no persistent state. It queries node resources from Proxmox at decision time, evaluates candidates, and returns a placement decision. If the orchestrator restarts, scheduling works immediately. There's no state to recover, no history to rebuild, no cache to warm up.
This means the scheduler always works with current reality. A node that was idle five minutes ago but just received a large VM will show its updated resource usage on the next scheduling decision. No stale data, no drift. For more on why this matters, see Stateless Orchestrator.
Scheduling constraints (planned)
The scheduler today handles the most common case well: spread VMs across your cluster by available memory. But real-world deployments have more nuanced requirements. These constraints are planned for future releases.
| Constraint | Purpose | Example |
|---|---|---|
| Node selector | Pin a VM to a specific host | GPU passthrough, hardware licensing |
| Affinity | Co-locate VMs on the same host | App server + sidecar cache |
| Anti-affinity | Keep VMs on separate hosts | Database primary + replica |
| Taints | Mark nodes to repel general workloads | Reserve GPU nodes for GPU VMs |
| Tolerations | Allow a VM to run on tainted nodes | GPU workload on a GPU-tainted node |
The vocabulary is borrowed from Kubernetes. If you've worked with nodeSelector, pod affinity, or taints and tolerations in Kubernetes, the concepts are the same, adapted for VMs instead of pods.
Scheduling constraints will be stored as Proxmox VM tags using a flxp/ prefix, keeping the metadata on the workload itself rather than in an external store. Tags survive migrations, are visible in the Proxmox UI, and don't depend on flex.plane being available.
Rebalancing and live migration (planned)
Initial placement handles where a VM starts. But workloads shift over time. A VM that was idle at creation might later consume significant resources, creating hotspots. Rebalancing detects these imbalances and migrates VMs to restore balance.
The core metric is simple, inspired by ProxLB's "balanciness" approach: measure the difference between the most-loaded and least-loaded node in a cluster. When this delta exceeds a threshold, the rebalancer identifies VMs to migrate and moves them to less-loaded nodes, converging the cluster toward an even distribution.
Rebalancing is a future capability. It requires live migration support, safety mechanisms (don't migrate too many VMs at once, respect constraints), and careful testing. The scheduler's pluggable interface is designed with this in mind. New strategies slot in without changing the placement pipeline.
The Agent
A lightweight daemon running on each Proxmox node. Acts as an authenticating reverse proxy to the Proxmox API, manages cloud image caching, handles routing, and bootstraps new nodes.
Virtual Datacenters
Multi-tenant isolation for Proxmox. Per-VDC resource quotas, network separation, and role-based access control let CSPs scope VMs, networks, and storage to teams or customers.