About this article: AI-assisted writing, reviewed against the cited sources. This is source research, not a lab validation of the procedures. Check the stated versions and your own recovery requirements before making changes.
Windows has no built-in virtio drivers, so the storage driver must be installed and bound to a real device before the boot disk moves to a virtio controller. Get that wrong and the VM stops at boot. Once it runs, the settings that matter most are the driver release, the controller type, IO threads, the cache mode and whether Hyper-V enlightenments are on. Most of the other options should stay at their defaults until a measurement says otherwise.
Identifiers are examples. Proxmox VE is the worked example; libvirt and QEMU differences are noted.
Interpretations are editorial; no lab testing was performed.
Why it matters
Emulated storage can impose overhead, although the effect depends on the workload and platform. Proxmox describes IDE as a design going back to the 1984 PC/AT controller, and highly recommends VirtIO SCSI or VirtIO Block for performance and because they are better maintained. After a migration from another hypervisor, a Windows VM often has to start on an emulated bus anyway: Proxmox states that only IDE or SATA work out of the box for Windows, with further steps needed to reach VirtIO.
The opposite risk is moving too fast. Driver releases have had serious storage faults, and the bad versions are specific. The release you install is a change to the storage path of the server and deserves the same care as a firmware update.
How it works
Virtio is a paravirtualised device interface. The guest knows it is virtual and exchanges requests with the host through shared queues, rather than having the host imitate real controller hardware. The VIRTIO 1.2 specification defines these queues and separate device types for block, SCSI host and network devices.
Each device type needs its own Windows driver. The virtio-win project supplies them, and the ISO holds one folder per driver with sub-folders per Windows version.
| Device on the VM | ISO folder | What Windows shows |
|---|---|---|
| VirtIO SCSI | vioscsi\2k25\amd64 |
Red Hat VirtIO SCSI pass-through controller |
| VirtIO Block | viostor, per the packaging script |
VirtIO SCSI controller, despite being a Block device |
| VirtIO network | NetKVM\2k25\amd64 |
Redhat VirtIO Ethernet Adapter |
| Memory balloon | Balloon\2k25\amd64 |
VirtIO Balloon Driver |
| QEMU guest agent | guest-agent\qemu-ga-x86_64.msi |
A Windows service |
Proxmox's block driver page calls that folder vioblk. The packaging script uses viostor, and the driver source installs viostor.sys, so we follow them. We could not inspect a real ISO, so confirm the folder on yours.
Choosing a driver release
The virtio-win packaging project publishes stable builds, which roughly match the most recent Red Hat Enterprise Linux release, and latest builds, which may be pre-release quality. Both links redirect to a versioned file. Use the versioned archive and record the exact number, so that every VM in a group can be reproduced.
Proxmox's driver page is the best public record found of which releases have caused trouble:
| Release | Status on Proxmox's page, read 2026-11-10 |
|---|---|
| 0.1.215 to 0.1.262 | VirtIO SCSI and Block: Event ID 129 warnings from vioscsi ("Reset to device, \Device\RaidPortN, was issued"), blocked I/O on the disk, in-guest reboots stuck at the spinner, and QEMU messages such as kvm: Desc next is 3. Moving to 0.1.266 or later should resolve it |
| 0.1.271 | No known issues |
| 0.1.285 | VirtIO SCSI and Block on Windows Server 2025: read errors and performance issues under heavy I/O, particularly SQL Server. 0.1.271 seems unaffected |
| 0.1.302 | Under test at 10 September 2026. No known issues, but Proxmox does not yet recommend it for production |
Re-read that page before you standardise, because the table will have moved on. Old operating systems are a separate case: newer releases may drop drivers for older Windows versions, and Proxmox names 0.1.189 for Windows Server 2012.
Signing and Secure Boot
Do not assume a VM with Secure Boot will accept the public builds. The packaging project says Windows 8 and later drivers carry Red Hat's test signature, Windows 10 and later drivers a Microsoft attestation signature, not a WHQL signature, and that WHQL builds come only with a paid RHEL subscription. It warns that drivers not signed by Microsoft will not load on some Windows versions with Secure Boot enabled. Microsoft's own page now describes attestation signing in terms of testing scenarios. The pages we opened do not settle which Windows Server releases accept which signature under Secure Boot, so prove it on a Secure Boot test VM with your exact ISO before relying on it.
What to choose and why
Getting the drivers in
New install. Add a second virtual CD drive holding the driver ISO. At the disk selection screen, choose Load driver, then browse to vioscsi, then repeat for NetKVM and Balloon. After first boot run virtio-win-gt-x64.msi from the ISO for anything still missing.
Existing VM, including one you have just migrated. This is where boot failures come from. Windows must see a disk requiring the driver before you switch. Installing the driver and switching the boot disk straight away gives an INACCESSIBLE BOOT DEVICE blue screen. Proxmox's procedure follows, with two additions of ours: the backup in step 1, and VirtIO SCSI single in step 2, where Proxmox says VirtIO SCSI or VirtIO Block.
- Back up the VM. Mount the virtio-win ISO.
- With the boot disk still on IDE or SATA, add a small disk (1 GB is enough) on the target bus, with the controller set to VirtIO SCSI single.
- In Windows, install the driver for the unknown device from the ISO. Wait until the small disk appears in Disk Management.
- Shut down. Detach and remove the small disk.
- Detach the boot disk and re-attach it on the SCSI bus.
- Set the boot order so the re-attached disk is first. Proxmox marks this step as important.
- Start the VM.
If it does not boot, re-attach the disk as IDE or SATA and redo the procedure, adapting the boot order again. Preserve the original attachment and backup, and change one setting at a time; a bus change alone does not establish a guaranteed rollback. If you convert several disks, they may need to be brought online in Disk Management on the first boot.
For a scripted install across many VMs, pnputil adds and installs driver packages from an elevated prompt. The drive letter here is an example:
pnputil /add-driver D:\vioscsi\2k25\amd64\*.inf /install
pnputil /enum-drivers
Look in the second command's output for the vioscsi package and its version. Staging a package this way does not, on its own, replace the small-disk step in the Proxmox procedure.
Conversion with virt-v2v. virt-v2v modifies the guest so it can boot on KVM, and copies in Windows drivers from VIRTIO_WIN or /usr/share/virtio-win. These points from its manual catch people out:
--block-driveracceptsvirtio-blkorvirtio-scsi, and the default isvirtio-blk. Present the same bus to the guest that you converted for.- A guest using Fast Startup or hibernation cannot be converted. Disable Fast Startup inside the guest and shut it down cleanly first.
- Antivirus and Group Policy can block the driver installation, and the manual recommends disabling or removing them before conversion.
- Virt-v2v runs unsigned first-boot PowerShell scripts, so an ExecutionPolicy such as AllSigned must be relaxed. The guest must be cleanly shut down first, and a system disk on a Dynamic Disk cannot be converted.
- Windows may reboot four or more times after conversion. Do not interrupt it, because interruption can stop the guest booting correctly.
Boot failure 0x0000007B means Windows could not access the system volume during startup; a missing or mismatched storage driver is one possible cause. Repair of a VM already in that state is a separate topic; Microsoft documents adding drivers to an offline image with DISM, which places packages in the driver store for Plug and Play to associate at boot.
Disk: controller and IO threads
Use VirtIO SCSI single, and set IO Thread yourself: that combination is the default only for newly created Linux VMs since Proxmox VE 7.3. Proxmox recommends it for the most features and best performance, and warns that VirtIO Block may be deprecated.
With IO thread enabled, QEMU creates one I/O thread per storage controller rather than handling I/O in the main loop or vCPU threads. VirtIO SCSI single gives each disk its own controller, and IO thread is only available on that controller type or on VirtIO Block. The single-controller layout matters: in libvirt, every SCSI disk on one controller shares that controller's IOThread, so separating disks requires separate controllers.
A Proxmox example in /etc/pve/qemu-server/101.conf form, with hypothetical values:
scsihw: virtio-scsi-single
scsi0: local-lvm:vm-101-disk-0,cache=none,discard=on,iothread=1,size=80G
net0: virtio=BC:24:11:12:34:56,bridge=vmbr0,queues=4
The libvirt equivalent, composed from documented attributes:
<iothreads>2</iothreads>
<controller type='scsi' index='0' model='virtio-scsi'>
<driver iothread='1'/>
</controller>
<disk type='block' device='disk'>
<driver name='qemu' type='raw' cache='none' discard='unmap'/>
<source dev='/dev/vg0/app01-os'/>
<target dev='sda' bus='scsi'/>
</disk>
Disk: cache mode
The cache mode decides when the guest is told a write is finished. QEMU defines each mode by three settings:
| Mode | Reports write cache to guest | Bypasses host page cache | Ignores flushes |
|---|---|---|---|
writeback |
on | off | off |
none |
on | on | off |
writethrough |
off | off | off |
directsync |
off | on | off |
unsafe |
on | off | on |
QEMU's own default is writeback, while Proxmox defaults to No cache. Check which applies to you, because moving a VM between management stacks can change the mode.
Proxmox's guidance is not uniform. Its Windows 2025 page, scoped to Proxmox VE 8.x, sets Write back for best performance and calls No cache safer but slower. Its Performance Tweaks page, written for raw volumes, says none seems best, and warns of none: "like writeback, you can lose data in case of a power failure". It calls its table only a general overview, since a battery-backed RAID cache works fine with writeback. Both modes report a write cache to the guest, so unflushed data can be lost in either. Safety depends on the guest issuing flushes and the storage honouring them. Our recommendation is still none for database and transactional VMs, because it bypasses the host page cache and removes one place data can sit. Try writeback only after measuring your workload and confirming a host crash is covered.
Never use unsafe on data you intend to keep. It ignores guest flushes. QEMU says that if the host loses power or the disk storage is disconnected, "your image will most probably be rendered unusable".
Image format also counts. Proxmox suggests raw especially for SQL Server files, as qcow2 can be very slow under that load. On block-based storage such as LVM, ZFS or Ceph, raw is required anyway.
libvirt calls detect_zeroes compute intensive, but says it "can save file space and/or time on slow media". Leave it off unless you have measured a benefit.
Disk: TRIM and space reclamation
Three layers must agree. The storage must support thin provisioning, the drive must have Discard enabled, and the guest must issue TRIM. In libvirt the equivalent is discard='unmap'.
For NTFS, Windows has trim enabled by default unless an administrator turned it off. On ReFS v2 trim is disabled by default, and on ReFS v1 it is enabled. A device that reports no trim support receives none. Check the setting, then reclaim space already freed with Optimize-Volume:
fsutil behavior query DisableDeleteNotify
Optimize-Volume -DriveLetter E -ReTrim -Verbose
A value of 0 means delete notifications are on. Run the re-trim outside busy hours, since it asks the storage to process every unused sector on the volume.
Two limits apply to the other bus and flag. Discard on VirtIO Block is documented as supported only on guests using Linux kernel 5.0 or later, some guests need SSD emulation set, and SSD emulation is not supported on VirtIO Block.
Network: multiqueue and RSS
Without multiqueue, one queue pair serves the NIC and throughput stops scaling with vCPUs. The specification has a single queue pair unless multiqueue or RSS is negotiated, and multiqueue is off until the driver enables it. So both ends need setting.
On the host, Proxmox's queues option runs from 0 to 64 and the recommended value equals the VM's vCPU count, meaning sockets times cores. In libvirt, set queues on the interface <driver>. In the guest, open the adapter's Advanced tab, confirm Receive Side Scaling is Enabled, and set Maximum number of RSS Queues to the vCPU count. Then read the result with Get-NetAdapterRss:
Get-NetAdapterRss -Name "Ethernet" | Format-List -Property "*"
Do not turn this on everywhere. Proxmox warns that values above one increase CPU load on host and guest as traffic rises, and recommends it only for VMs handling a great number of incoming connections, such as routers, reverse proxies and busy HTTP servers.
Changing a NIC model changes the adapter Windows sees. Proxmox suggests considering removing static network configuration before migration, because Windows warns when the same address was set on a previous adapter, even one no longer present. Record the address, gateway and DNS servers first.
Hyper-V enlightenments
This setting is often missed, and it matters for CPU use more than for I/O. Windows knows how to cooperate with a Hyper-V host, and KVM can present that interface. QEMU states that no enlightenments are enabled by default in KVM or QEMU, and recommends enabling all currently implemented ones for best performance, unless specific requirements apply, such as migration to older QEMU/KVM versions, with named exceptions and conditions:
hv-syndbg,hv-passthroughandhv-enforce-cpuidshould not be enabled in production. They are debugging features, andhv-passthrougheffectively prevents migration.hv-resetcan be avoided.hv-evmcsis for Intel CPUs only.hv-no-nonarch-coresharingis only for properly pinned vCPUs.hv-reenlightenmentneeds hardware with TSC scaling, or a guest that will not migrate.hv-avicandhv-apicvneed hardware APIC virtualization.hv-spinlocksshould be set to, for example,0xfffwhen host CPUs are overcommitted.
Two are worth knowing by effect. hv-time gives a per-guest reference clock and a significant speed-up of timestamp operations. Without hv-stimer, certain Windows versions fall back to HPET or RTC, which can cause significant CPU use even when the vCPU is idle. hv-stimer depends on hv-vpindex, hv-synic and hv-time.
One side effect catches people during a crash investigation. QEMU says hv-crash must only be enabled if crash information is consumed through QAPI by higher layers of the stack. With the default -action panic=shutdown, it effectively prevents Windows from writing crash dumps. So leave it off unless your platform consumes that data, despite the general advice to enable everything.
In libvirt, set <hyperv> flags under <features> and add the hypervclock timer. On Proxmox, the admin guide does not list which flags each OS type sets, so read what is really being passed:
qm showcmd 101 --pretty
qm showcmd prints the command line used to start the VM. Look at the -cpu argument for the hv_ flags.
Memory ballooning
Two statements need reading together. The balloon device is added even with fixed memory, because it reports how much memory the guest really uses, and Proxmox generally advises leaving it enabled. But for Windows the driver is added manually and can slow the guest, so Proxmox does not recommend using it on critical systems.
Our reading is to install the driver for reporting, and to give critical systems such as SQL Server a fixed memory size, with the minimum equal to the maximum, so no reclamation takes place.
Risks and limits
A safe order of work
- Back up the VM and record the IP configuration.
- Choose and record one driver release. Test it on a copy under realistic load, as Proxmox recommends.
- Install all drivers and the guest agent while the VM still boots on the old bus.
- Move the boot disk using the small-disk procedure.
- Check Windows sees the expected controller, then move the NIC to virtio.
- Apply one tuning change at a time, measuring before and after.
- Remove the ISO and any temporary disk.
Evidence that narrows the cause
| Symptom | Check | What it shows |
|---|---|---|
INACCESSIBLE_BOOT_DEVICE after a bus change |
Re-attach as IDE or SATA and boot | Boots: storage-path differences are a leading hypothesis; this does not prove disk health or a unique driver cause |
Event 129 from vioscsi, stalled disk |
Installed driver version against the release table | In the 0.1.215 to 0.1.262 range: upgrade to 0.1.266 or later; 0.1.271 had no known issues on 2026-11-10 |
| Read errors under SQL load on Server 2025 | Driver version | On 0.1.285: Proxmox's page lists 0.1.271 as a workaround |
| Unknown device in Device Manager | Update driver, browse to the ISO with subfolders ticked | Missing driver |
| High idle CPU in the guest | qm showcmd, or virsh dumpxml for <hyperv> |
Establish whether relevant enlightenments are present; absence alone does not prove the cause |
| Thin volume does not shrink | Discard setting, DisableDeleteNotify, then -ReTrim |
Which layer is not passing the request |
| One vCPU saturated by network traffic | Host queues and guest RSS values |
Whether both ends are set |
Limits of this article
Proxmox's wiki pages vary in age. Its network driver page still says the drivers are unsigned, which conflicts with its driver page. We have relied on the newer text.
Red Hat's RHEL virtualisation guides and the Fedora virtio guide could not be opened automatically, so this article does not state their tuning advice. No throughput or latency figures are given because none were measured.
Sources
Opened 2026-11-10.
- Proxmox VE: QEMU/KVM virtual machines and qm manual, version 9.2
- Proxmox wiki: Windows VirtIO Drivers
- Proxmox wiki: Paravirtualized Block Drivers for Windows
- Proxmox wiki: Windows 2025 guest best practices
- Proxmox wiki: Performance Tweaks
- Proxmox wiki: Migrate to Proxmox VE
- virtio-win packaging README and packaging script
- viostor driver INX file
- Proxmox wiki: Paravirtualized Network Drivers for Windows
- NetKVM driver documentation
- OASIS: VIRTIO 1.2
- QEMU: Hyper-V Enlightenments
- QEMU: invocation and multiple IOThreads
- libvirt: domain XML format
- virt-v2v manual
- Microsoft: PnPUtil, DISM offline drivers, attestation signing, fsutil behavior, Optimize-Volume, Get-NetAdapterRss
This article was originally published on .