Skip to main content
RunBook Academy

Proxmox VEXXVI · Windows GuestsFleets of Windows guests

Windows guest performance

Expert⏱ ~35 minqmpvesh

What you'll learn

  • Choose a CPU type for a Windows guest and state what the kvm64 default costs
  • Size vCPUs for a Windows workload and explain why more is often slower
  • Select a cache mode deliberately, against the Proxmox default rather than around it
  • Weigh the balloon driver against the documented warning about it
  • Measure a Windows guest from both sides and reconcile the two answers

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.

“The Windows VM is slow” is one of the least informative sentences in virtualisation, and it arrives more often than any other performance report. Part XVII gave you a general method for it. This lesson is about the parts that are specific to Windows guests — where the defaults are wrong, where the documented advice contradicts what people assume, and why the number inside the guest and the number on the host so often disagree.

Start from an uncomfortable fact: the majority of Windows performance tickets in a healthy estate are not caused by anything Windows-specific. They are caused by host contention, storage that is saturated by a different guest, or a vCPU count that somebody increased in an attempt to fix the first two. Confirm the host is healthy before you touch the guest, using Part XVII. Everything below assumes you have.

CPU: the default is portable, not fast

cputype defaults to kvm64, which the administration guide describes as a model that “works on essentially all x86_64 host CPUs”. That is the whole design goal — it is a lowest-common-denominator model chosen so a VM can live migrate between a ten-year-old Xeon and a current EPYC without the guest noticing.

What it costs is every instruction set extension added since roughly 2007. For a Windows guest that means the AES instructions BitLocker and TLS want, the vectorised paths that compression, media handling and modern .NET code paths take, and the population-count and bit-manipulation instructions that show up in hot loops all over the place. None of this produces an error; it produces software silently taking a slower path.

The other end is host:

If you want an exact match, you can set the CPU type to host in which case the VM will have exactly the same CPU flags as your host system.

with the documented caveat:

If you want to do a live migration of VMs between different hosts, your VM might end up on a new system with a different CPU type or a different microcode version.

The Proxmox best-practice pages for Windows Server 2022 and 2025 both recommend host. For a homogeneous cluster that is straightforwardly right. For a mixed cluster the honest choice is a named model that every node supports — the oldest generation in the cluster — which gets you most of the instruction sets without making migration a gamble. Part IX covers the model selection; the Windows- specific point is only that kvm64 costs Windows more than it costs a typical Linux guest, because more of the Windows userland is compiled to take advantage of what it finds.

ostype is a performance setting

This is the third time Lesson 1’s ostype field appears, and here is why. The qm.conf reference describes it as:

Specify guest operating system. This is used to enable special optimization/features for specific operating systems.

Optimisations, not labels. The KVM hypervisor can present Windows-specific interfaces to a guest that recognises them, and Proxmox decides whether to do so from the guest type. A related option corroborates this: --cpu carries a hv-vendor-id sub-option, a custom Hyper-V identifier string — an option that would have no reason to exist unless Windows guests were being given a Hyper-V-compatible interface to talk to.

The practical instruction is unchanged and now has a third reason behind it: set ostype correctly. A Windows guest declared other runs with generic settings on a hypervisor that had better ones available.

vCPUs: more is frequently slower

The administration guide is relaxed about overcommit in aggregate:

It is perfectly safe if the overall number of cores of all your VMs is greater than the number of cores on the server […] However, Proxmox VE will prevent you from starting VMs with more virtual CPU cores than physically available.

That is a statement about the host. It is not permission to give a single guest more vCPUs than it can use, and on Windows the penalty for doing so is larger than intuition suggests.

The rules that hold up in practice:

  1. Start at 2 or 4 vCPUs and increase only with evidence. A guest that is 90% idle on four vCPUs will be 95% idle on eight and slightly slower.
  2. One socket unless you need NUMA. --sockets 1 --cores 8, not --sockets 8 --cores 1. Multiple virtual sockets imply a NUMA topology to the guest, and Windows will schedule as if the topology were real.
  3. --numa 1 only for guests large enough to span host NUMA nodes, and then with a topology that matches the host. The option defaults to 0. A guest smaller than one host node gains nothing and inherits a scheduling constraint.
  4. Windows Server licensing is per physical core on the host, not per vCPU — but many application licences are per vCPU. Check before you double a guest.

Storage: controller, cache, and one honest tradeoff

The controller is settled: virtio-scsi-single, from Lesson 1, with the system disk on scsi0. “Single” gives each disk its own controller, which is what makes iothread=1 meaningful — a dedicated I/O thread per disk rather than contention on one.

Cache mode is where the documentation and the wiki appear to disagree, and it is worth resolving rather than picking one.

The administration guide describes the default:

No cache (default) […] the guest system will be notified that a write is complete when each block reaches the physical storage write queue

and calls it “a good balance between safety and speed”. The Windows best-practice wiki pages recommend Write back “for optimal performance”.

Both are correct, for different questions:

ModeWrite is acknowledged whenUse when
none (default)it reaches the storage write queue, bypassing the host page cacheYou want the documented balance and predictable behaviour under host failure
writebackit reaches the host page cacheThroughput matters more than the window of exposure, and the storage is not already caching
writethroughit is on stable storage, with reads cached on the hostRead-heavy guests where write latency is not the constraint
directsyncit is on stable storage, no host caching either wayDatabases with their own durability requirements, or clustered storage
unsafeimmediately, ignoring guest flushesNever on anything you would miss. Template builds and throwaway test guests only

The remaining disk options are less contentious. discard=on lets the guest’s TRIM reach thin-provisioned storage, which matters on ZFS, LVM-thin and Ceph; ssd=1 presents the disk as non-rotational so Windows enables TRIM and disables the defragmentation schedule it would otherwise apply to a “spinning” disk; iothread=1 gives the disk its own thread. The aio sub-option accepts io_uring, native or threads, and Part IX covers when to move off the default.

The balloon driver: a documented tradeoff

Everything else in this lesson is a straightforward win. This one is not, and the administration guide says so without hedging:

For Windows OSes, the balloon driver needs to be added manually and can incur a slowdown of the guest, so we don’t recommend using it on critical systems.

Take that at face value. It is an unusual thing for a vendor to write about its own feature, and the reason is real: reclaiming memory from a Windows guest means the balloon driver allocates pages the guest then cannot use, and Windows responds to memory pressure by trimming working sets and paging — inside the guest, to a virtual disk, on shared storage.

The distinction that resolves this in practice is the one from Lesson 2:

  • The balloon driver enables reclaim. This is the part with the performance cost, and it only has a cost when the host actually reclaims.
  • The balloon service enables reporting. This has no performance cost at all, and it is what makes the Proxmox memory figure match Task Manager.

So the useful configuration for a critical Windows guest is: install both, and set a floor that prevents reclaim. The balloon option is documented as the “amount of target RAM for the VM in MiB”, and the driver is enabled by default unless explicitly disabled with zero. Setting balloon equal to memory gives you accurate reporting with no room for the host to take anything back.

Configuration changeaccurate memory reporting without reclaim
VMID=9100
qm set "$VMID" --memory 16384 --balloon 16384
qm config "$VMID" | grep -E '^(memory|balloon):'

For non-critical guests — test machines, seldom-used application servers, a desktop estate — a lower floor is a legitimate way to increase density, and the slowdown the guide warns about is a slowdown on a machine nobody is waiting for. Decide per guest, not per estate, and write the decision down.

Measuring, from both ends

A performance claim needs two measurements that agree. Guest-side numbers alone are unreliable under contention for the reason in the Under the Hood box: a guest cannot see time it did not get.

From the host, per-VM resource use over time:

Read-only / Safehourly CPU, memory and disk figures for one guest
# pvesh get /nodes/pve1/qemu/9100/rrddata --timeframe hour --output-format json | head -20
[
 {
    "cpu" : 0.184,
    "disk" : 0,
    "diskread" : 45056,
    "diskwrite" : 2686976,
    "maxcpu" : 4,
    "maxdisk" : 68719476736,
    "maxmem" : 17179869184,
    "mem" : 5837107200,
    "netin" : 88192,
    "netout" : 141312,
    "time" : 1786000000
 }
]

Illustrative output

Two things to read: cpu is a fraction of the guest’s total vCPU capacity, so 0.184 on maxcpu: 4 is roughly three quarters of one core; and mem is only meaningful if the balloon service is installed — otherwise it is close to maxmem for the reasons in Lesson 2.

From the guest, the counters that answer the question the ticket is really asking. Processor time tells you almost nothing on its own; queue lengths and latency tell you where the wait is:

Read-only / Safethe four counters worth sampling first
Get-Counter -Counter @(
  '\Processor(_Total)\% Processor Time',
  '\System\Processor Queue Length',
  '\PhysicalDisk(_Total)\Avg. Disk sec/Read',
  '\PhysicalDisk(_Total)\Avg. Disk sec/Write',
  '\Memory\Available MBytes',
  '\Memory\Pages/sec'
) -SampleInterval 1 -MaxSamples 60 |
Export-Counter -Path C:\perf\baseline.blg -Force

Key takeaways

  • kvm64 is the portable default and costs a Windows guest every instruction set added since the mid-2000s. Use host on a homogeneous cluster, or the oldest common named model on a mixed one.
  • ostype is documented as enabling special optimisations for specific operating systems. It is a performance setting, not a label.
  • More vCPUs is often slower. Lock-holder preemption scales with the number of contending vCPUs, and its signature is high guest CPU with no extra work done.
  • Cache mode is a real decision with a real exposure. writeback acknowledges writes into the host page cache; guest flushes are still honoured, so the loss window is unflushed data at the moment the host dies.
  • Proxmox does not recommend the Windows balloon driver on critical systems. Install the balloon service everywhere for reporting, and set balloon equal to memory on guests where reclaim would hurt.
  • Guest-observed latency includes time the vCPU was not running. Check the host before you believe a guest-side storage number.

Knowledge check

Knowledge check · 4 questions

  1. Q1. A Windows application server on 8 vCPUs shows 80% processor time across all vCPUs in Task Manager while serving its usual request rate. Host CPU is moderate. What is the most likely explanation and the correct response?

  2. Q2. You are asked to enable cache=writeback on a Windows guest that runs a transactional database, on the grounds that the Proxmox best-practice wiki recommends write back for Windows. What is the correct answer?

  3. Q3. A Windows guest reports 40 ms average disk read latency while the host shows the underlying device answering in 2 ms and far from saturated. Which of these are genuine possible occupants of the gap? Select all that apply.

  4. Q4. Because Proxmox does not recommend the Windows balloon driver on critical systems, the correct configuration for a critical Windows guest is to leave the balloon device and its service out entirely.

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