Skip to main content
RunBook Academy

Proxmox VEXVII · Performance EngineeringCapacity tuning

VM right-sizing: matching resources to workloads

Intermediate⏱ ~20 min🧪 Lab required

What you'll learn

  • Profile a workload to determine its real resource needs
  • Right-size CPU, RAM, and disk for typical workload patterns
  • Use ballooning and hot-plug to handle dynamic resource needs
  • Avoid the two common over-provisioning traps

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-07

Not yet marked complete on this device.

VM right-sizing: matching resources to workloads

Most VMs in production are over-sized. A 16 GB VM that uses 3 GB is wasting 13 GB. With 100 such VMs, you’re burning 1.3 TB of RAM that could host 30+ additional workloads.

This lesson shows how to measure actual workload needs and right-size VMs without sacrificing safety margin.

The default sizing trap

PVE’s default VM creation provides generous CPU and memory — usually 1 core and 1 GB minimum. For a real workload, neither is enough. So admins bump up to “production-sized”: 4 cores, 8 GB, 100 GB disk.

The result: most workloads run with low utilisation. A web server peaks at 1.5 cores and 4 GB of RAM but is allocated 4 cores and 8 GB. The unused 2.5 cores and 4 GB of RAM could host more VMs on the same hardware.

The cost of over-provisioning is invisible but real:

  • Less density per host (fewer VMs per host)
  • More hosts needed (more licensing, power, cooling)
  • Slower live migration (more memory to transfer)
  • Larger backup windows (more data per VM)

The sizing workflow

A right-sizing pass for one VM:

  1. Measure current utilisation — run for at least 7 days at peak workload
  2. Profile memory usage patterns — distinguish RSS (actual use) from cache and buffer
  3. Add safety margin — 50% for RAM, 25% for CPU
  4. Apply and monitor — verify no degradation
  5. Re-tune after 30 days

Measuring CPU utilisation

Use the qemu-guest-agent from inside the VM, or node_exporter from the host:

# Host-side (via PVE API)
pvesh get /nodes/pve-01/qemu/100/status/current
# Look at "cpu" (current usage 0.0–1.0) and "cpus" (allocated count)

# Guest-side
top
# Look at %CPU column under load
mpstat 1 5
# 1-second samples, 5 iterations

A VM’s CPU utilisation is reported as a fraction of one core. A 4-core VM at 100% utilisation shows as “cpu”: 4.0 in the PVE API.

For a web server under typical load:

  • Idle: 0.05–0.10 (5–10% of one core)
  • Average: 0.20–0.40
  • Peak (cache miss, request burst): 0.80–2.0

Right-sizing: aim for peak ≤ 75% of allocated cores. So if peak is 2.0, allocate 3 cores.

Measuring memory utilisation

# Inside the VM
free -h
vmstat 1 5
# Look at:
#   - "si/so" (swap in/out) — should be 0 or near-0
#   - "bi/bo" (block I/O) — high values mean memory pressure
#   - "cache" — OS uses free RAM for cache; that's normal

# More detailed
ps aux --sort -rss | head
# Top processes by RSS

# Tracking over time (collect into a file)
for i in $(seq 1 60); do
  free -m | awk '/^Mem:/ {print $3}' >> /tmp/mem-used.log
  sleep 60
done

The numbers to focus on:

  • RSS (resident set size) — actual physical memory used by processes. This is what you care about.
  • Cache — OS uses free RAM as page cache. Don’t count this as “used” — it’s released automatically when apps need it.
  • Swap in/out — non-zero swap means the VM is under memory pressure and is hurting itself.

A web server under typical load:

  • RSS: 1.5–2.5 GB
  • Cache: variable, often equal to or larger than RSS
  • Swap in/out: 0

Right-sizing RAM: aim for peak RSS × 1.5. So if peak RSS is 2 GB, allocate 3 GB.

Disk sizing

Disk sizing is straightforward — measure current usage, add growth margin, project over time.

# Inside the VM
df -h
du -sh /var /home /opt /tmp 2>/dev/null

# Host-side (for thin-provisioned disks)
pvesh get /nodes/pve-01/qemu/100/config \
  --output-format json | jq '.rootfs'

# Or via the GUI: VM → Hardware → Hard Disk → "Disk size" vs "Disk usage"

For thin-provisioned disks, allocate based on growth forecast, not current usage. A VM using 20 GB today will likely need 100 GB in two years. Allocate 200 GB thin-provisioned; the storage backend only allocates what’s actually used.

For thick-provisioned disks (rare), allocate current usage plus forecast.

Ballooning for dynamic sizing

The qemu guest agent’s balloon driver (virtio-balloon) allows the host to reclaim memory from a VM that’s not using it, and give it back when needed.

# Enable ballooning on a VM
qm set 100 --balloon 0   # 0 = ballooning disabled
qm set 100 --balloon 50  # 50% minimum memory (1 GB on a 2 GB VM)

The VM’s qemu-guest-agent reports its actual memory usage to the host. If the VM is using 800 MB of a 2 GB allocation, the host can inflate the balloon to reclaim up to 1.2 GB.

Ballooning is great for:

  • Mixed workloads where some VMs are idle at any given moment
  • Cloud-like elasticity on a fixed pool
  • Cost-sensitive deployments

Ballooning is bad for:

  • Performance-sensitive workloads (balloon pressure adds latency)
  • VMs without qemu-guest-agent installed

Set balloon to 0 (disabled) for databases and other latency-sensitive workloads.

CPU hot-plug

For VMs that need CPU dynamically:

# Allocate a CPU pool
qm set 100 --cores 4 --sockets 1

# Hot-plug to 6 cores (if the guest supports it)
qm set 100 --cores 6
# The guest kernel sees the new cores immediately

# Hot-unplug back to 4
qm set 100 --cores 4

Hot-plug works for most modern Linux kernels (CPU hot-plug is well supported) but not for Windows without integration services.

For RAM hot-plug:

qm set 100 --memory 4096
# Most Linux guests auto-online the new memory; some need:
# echo 1 > /sys/devices/system/memory/memoryXXX/online

For production VMs that don’t need to dynamically scale, use fixed sizes. Hot-plug adds complexity.

Sizing templates by workload type

Common sizing patterns:

WorkloadCPURAMDiskNotes
Reverse proxy / load balancer1 core1 GB10 GBNetwork I/O bound, scale horizontally
Web server (static)2 cores2 GB20 GBCache-friendly, may need more disk for content
Web server (dynamic)2 cores4 GB50 GBDB calls, sessions
App server (Java)4 cores8 GB30 GBJVM heap typically 4 GB
App server (Go / Rust)2 cores1 GB20 GBLean runtime
Database (Postgres)4 cores16 GB100 GBshared_buffers = 25% RAM
Database (Redis)4 cores8 GB50 GBAll-in-memory, plan for dataset size
PBS server4 cores8 GB2 TBChunk storage, garbage collection matters
CI runner4 cores8 GB50 GBBursty load

These are starting points. Always measure in production and adjust.

Right-sizing at scale

For a fleet of 100+ VMs, manual sizing doesn’t scale. Use:

  • Capacity planning tools (VMPatrol, vCenter) — though PVE doesn’t have an equivalent built in
  • Grafana dashboards showing per-VM peak utilisation
  • Ansible / config management to apply sizing rules
  • Quarterly reviews of the top 10 over-provisioned VMs

The metric to track: average utilisation / average allocation. Aim for 60–70% — under 50% means over-provisioning; over 80% means under-provisioning.

Common mistakes

  • Sizing for the busiest day of the year. Most workloads have peaks. A web server that hits 3 cores on Black Friday doesn’t need 3 cores year-round. Use auto-scaling (more VMs) or scheduled right-sizing.
  • Allocating RAM as “I might need it later”. Memory that’s allocated to a VM is unavailable to others. Allocate what the workload needs today, with margin for predictable growth.
  • Ignoring NUMA. On multi-socket hosts, a VM that crosses NUMA boundaries has memory access latency penalties. Pin the VM to a single NUMA node (qm set 100 --numa 1 with appropriate topology).
  • Forgetting balloon and hot-plug costs. Both add a small amount of CPU overhead. For performance-critical VMs, disable them.

Production considerations

  • Over-commit carefully. PVE supports CPU over-commit (host has 16 cores, VMs allocated 32 cores total) but not RAM over-commit by default. Track per-host RAM committed vs available.
  • Sizing is iterative. Right-size, monitor for 30 days, adjust. Don’t expect to get it right on the first pass.
  • Document your sizing decisions. A spreadsheet or wiki with “VM X is sized 2 cores / 4 GB because workload Y peaks at Z” is invaluable when you’re asked to justify costs or plan capacity.

Key takeaways

  • Measure first, then size. Aim for peak utilisation ≤ 75%.
  • Use balloon for elastic workloads, disable for databases.
  • Right-sizing is iterative — measure, apply, monitor, adjust.
  • Track utilisation, not allocation, to detect over-provisioning.

Knowledge check

Knowledge check · 4 questions

  1. Q1. Why should ballooning be disabled on database VMs?

  2. Q2. A web server peaking at 1.5 cores should be allocated 4 cores.

  3. Q3. Which of these are appropriate right-sizing practices? (Select all that apply)

  4. Q4. What is the safety margin for RAM when right-sizing?

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