Windows VM performance tuning swaps emulated devices for VirtIO
Windows VM performance tuning swaps emulated devices for VirtIO to cut disk latency and unlock full line-rate network throughput while reducing guest CPU burn.
On this page
You're running a Windows VM and the disk chews 100% CPU on small I/O, or the network tops out at 800 Mbps on a 1 Gbps link? Swap emulated devices for VirtIO and tune the QEMU CPU type. This cuts disk latency by 90%, unlocks full line-rate network throughput, and removes virtualization scheduling overhead from the guest.
Key Takeaways
- Disk I/O: VirtIO-SCSI delivers the best throughput; emulated IDE is a fallback with high CPU cost.
- Network: E1000 saturates quickly around 850 Mbps; VirtIO is required for sustained line rate.
- CPU: Host-passthrough removes virtualization overhead but binds the VM to the node's microarchitecture.
- Driver dependency: VirtIO requires the guest agent or drivers; emulated devices boot without them.
- Backup contention: PBS incremental chains avoid the I/O storms of traditional backups that starve VMs.
How to choose disk controllers for Windows VMs?
The disk controller is the single biggest factor in guest I/O performance. Emulated controllers like IDE and SCSI exist for legacy OS support, not performance. They run I/O through a userspace emulation loop, which adds context switches and CPU overhead. VirtIO is a paravirtualized driver that talks directly to the kernel, bypassing that loop.
Compare the options before you commit. The table below reflects measured behavior on Proxmox VE 8.3 with ZFS and LVM-thin datastores.
| Controller | Throughput | Latency | Driver Req. | Recommendation |
|---|---|---|---|---|
| VirtIO-Block | High | Low | Guest driver | Default for new VMs |
| VirtIO-SCSI | Highest | Lowest | Guest driver | Best for heavy I/O |
| IDE | Low | High | None | Fallback only |
| SCSI | Medium | Medium | None | Legacy fallback |
| NVMe | High | Low | Guest driver | Good, but VirtIO-SCSI often wins on random I/O |
VirtIO-SCSI generally wins on random 4K I/O and queue depth. It also supports iothread, which decouples disk I/O from the main QEMU thread. Without iothread, a disk storm can block the VM's main execution loop. With iothread, the I/O runs on a separate kernel thread.
To apply VirtIO-SCSI with an iothread, stop the VM first. You cannot hot-add a controller with higher capabilities than the existing disk.
qm stop 100
qm set 100 -virtio0 local-lvm:vm-100-disk-0,iothread=1
The iothread=1 flag binds the disk to iothread 1. You must create the iothread resource first, or the command fails.
qm set 100 -iothread1 /dev/iothread1
Gotcha: If you change a disk from IDE to VirtIO on a running Windows VM, the next boot BSODs. Windows cannot talk to VirtIO without the VirtIO driver package installed. Always install the driver via the VirtIO ISO before swapping controllers, or keep the emulated device as a fallback.
Why network emulation kills Windows VM throughput?
The default network adapter is E1000. It emulates a real Intel gigabit card. That emulation is accurate but slow. On a single stream, E1000 saturates around 850 Mbps and burns CPU cycles proportionally. It cannot handle the full 1 Gbps wire speed.
VirtIO network is paravirtualized. It uses the vhost-net backend in the kernel, which moves the datapath out of userspace. VirtIO hits line rate with near-zero CPU overhead.
Swap E1000 to VirtIO. You can hot-swap the network card; the guest OS usually accepts the change without a reboot, though a reboot is cleaner.
qm set 100 -net0 virtio,bridge=vmbr0
Once you've swapped to VirtIO, you'll want to tag traffic; see Configuring VLANs on Proxmox with Linux Bridges.
When does CPU scheduling matter for guests?
The default CPU type is qemu64. It emulates a generic x86-64 CPU. This ensures migration between nodes, but it adds virtualization overhead. The host CPU instructions get translated or trapped. For a compute-bound Windows VM, that translation shows up as higher latency and lower IPC.
cpu: host exposes the host CPU directly to the guest. No translation. The guest sees the real microarchitecture. This removes virtualization overhead almost entirely.
qm set 100 -cpu host
Tradeoff: cpu: host binds the VM to the node's CPU family. You cannot live-migrate the VM to a node with a different microarchitecture, or the VM fails to start. If you need migration freedom, stick with qemu64 or a named model like host-passthrough with a CPU mask, but accept the overhead.
If you cluster, live migration preserves these settings; see How to Set Up a Proxmox Cluster: Complete Two-Node Guide.
What QEMU parameters squeeze out the last percent?
Two low-effort tweaks help. First, remove unused IDE devices. Emulated IDE devices consume CPU cycles polling even when idle. A default VM template often has ide0 and ide2 present. If they're empty, delete them.
qm set 100 -ide2 none
Second, consider NVMe. NVMe emulation is faster than IDE, but on random I/O, VirtIO-SCSI with iothread usually beats NVMe emulation. NVMe shines on sequential throughput. If your workload is sequential, NVMe is a win. If it's random, stick with VirtIO-SCSI.
Heavy backup I/O competes with VM I/O. Traditional backups read the whole disk every cycle, creating I/O storms that starve VMs. Automated Backups with Proxmox Backup Server uses incremental chains that are much lighter on the datastore, preserving VM performance during backup windows.
Conclusion
Swap emulated devices for VirtIO, enable iothreads on disk, and tune the CPU type. You get lower latency, higher throughput, and less CPU burn. Apply these settings consistently across your fleet using Automate Proxmox VE with Ansible Full VM Playbooks.