Skip to the content.

Windows (or Linux) in Linux with KVM/QEMU

TOC

  1. Windows (or Linux) in Linux with KVM/QEMU
    1. TOC
    2. Introduction
    3. Installation
    4. System Setup
    5. Setup VM and Install Windows 10/11
    6. Linux Guest
    7. KVM Tuning
      1. General Tuning
      2. Windows-Only Tuning
    8. Configuration Files
    9. virt-manager Usage
    10. Virsh Command Line Management Interface
    11. Networking
      1. Bridge Networking
      2. NAT
      3. Host-Only Network
    12. Host Device Forwarding
    13. Files and File System Access
    14. Further Commands and Documentation
    15. Windows XP Notes
    16. Direct QEMU Usage
    17. Errors

Introduction

There are a lot of guides out there explaining how to run Windows 10 in a Linux VM. Unfortunately all the guides I found did lack a few pieces of the puzzle, which I had to find somewhere else.

That’s why I (hopefully) collected everything here, for the next to use.

Base System/tested with

The guides I used as starting point were

Installation

Packages to install

System Setup

Add user to libvirt group

When installed dnsmasq and not used, disable it

If you don’t want to run LibVirt all the time use the helper scripts to start virtualisation support on demand

  1. Install root start/stop script kvm_qemu/kvm_services to
    /usr/local/sbin/kvm_services
  2. Allow sudo invocation without password
    YOUR_USERNAME ALL = NOPASSWD: /usr/local/sbin/kvm_services
  3. Call once to stop all system services
    sudo /usr/local/sbin/kvm_services stop sudo /usr/local/sbin/kvm_services disable
  4. Put user control script kvm_qemu/kvm to a location excutable from the user, e.g. ~/bin/kvm
  5. Start everything with: kvm start
  6. Stop with: kvm stop
  7. Show more commands with: kvm --help
  8. Optional create VM starter script, based on kvm_qemu/vm_start to a location excutable from the user, e.g. ~/bin/kvm

Configuration changes

Setup VM and Install Windows 10/11

  1. Download Windows 10 image
  2. Download Guest drivers
  3. Create VM with virt-manager [–debug]
    1. When QEMU/KVM not visible, add with File/Add Connection
    2. Name: Windows_10
    3. Storage: Create custom storage
      • Green “plus” bottom-left: Create storage pool
        • Name: kvm_storage
        • Type: dir
        • Path: /opt/kvm
      • Create new volume in this pool: green “plus” near “Volumes”
        • qcow2 Format
      • Attention: Storage file must be accessible by user libvirt-qemu
    4. Select “Customize before install”
      • SATA Disk 1/Avanced Options/Disk bus
        • SATA –> VirtIO
        • Optional: Cache mode to writeback for better performance (and less safety)
      • NIC/Device model: e1000e –> virtio
      • Mount VirtIO driver ISO
        • Add Hardware: Storage
          • Manage: /Path/to/virtio-win.iso
          • Device Type: CDROM
      • Boot options
        • Enable Boot Menu
        • Order: CDROM 1 - Disk 1 - CDROM 2
      • CPUs
        • Current Allocation: 6
        • Copy host CPU configuration
      • Memory: 8192 MiB
      • Add Hardware / Channel
        • For clipboard sharing with spice-guest-tools
        • Name: “com.redhat.spice.0”
        • Device type: “Spice agent (spicevmc)”
      • Use QXL as Video driver, otherwise “Auto resize VM with window” (Enable in virt-manager menu View / Scale Display)
      • Increase video memory to support higher resolutions: XML video / model type=’qxl’ / vgamem=’16384’ –> vgamem=’65536’
  4. Enable (U)EFI boot
    1. When Importing from Virtualbox required with setting “System/Enable EFI”
    2. Install dependency ovmf
    3. During Setup
      • Overview / Hypervisor Details
      • Chipset: Q35
      • Firmware: UEFI x86_64: /usr/share/OVMF/OVMF_CODE_4M.fd
  5. Add TPM (Trusted Platform Module) 2.0 device
    • Required for Windows 11
    • Install packages: swtpm swtpm-tools
    • Note for Debian 12: May require “sudo chown -R tss /var/lib/swtpm-localca” to start - systemctl restart libvirtd.service # Needed? - Add Hardware / TPM: Emulated, Model CRB, Version 2.0
  6. Setup Networking
  7. Start network before machine start in virt-manager
    • Win10/Edit/Connection Details/Virtual Networks
    • Start/Stop
  8. Start Installation Windows 10
    • Load file driver from CDROM 2: viostor/w10/amd64
    • Load network driver from CDROM 2: drive/NetKVM/w10/amd64
    • Installation Notes
      • First account created during installation has always local admin permissions
      • Create user account without admin rights (Settings/Accounts)
      • Uninstall apps: Settings/Apps/Apps&Features + Optional features
    • After Windows 10 installation is done, install the VirtIO tools and drivers
      • Start/Device Manager/Other devices/*
      • Search CDROM 2 for drivers
    • Install SPICE Guest tools from
  9. Afterwards: Disable boot menu and boot from CDROM
    • Virtual Machine/View/Details (or 2. button)
    • Boot options
      • Disable Boot Menu
      • Order: Disk 1
    • Remove CDROM 1 + 2
  10. Install KVM Virtio driver and guest tools
    • https://pve.proxmox.com/wiki/Windows_VirtIO_Drivers
    • From ISO: https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso
      • Or https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/virtio-win.iso
      • virtio-win-gt-x64, virtio-win-guest-tools
    • Or use direct installer
      • https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/
      • Or https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/
      • virtio-win-gt-x64.msi, virtio-win-guest-tools.exe
    • Not needed anymore: SPICE Guest tools
  11. virt-manager Setup
    • Win10/Edit/Connection Details
      • Storage
        • Remove Autostart/On Boot for all
        • Delete default - can also delete /var/lib/libvirt/images
        • Delete tmp
        • Delete installation media
      • Virtual Networks
        • Remove Autostart/On Boot for all
  12. SSH Server in Windows
    • For simple file sharing
      • Use e.g. Samba for more comfortable sharing
    • Settings/Apps/Apps&Features/Optional Features/Add a feature/OpenSSH Server
    • Start Server
      • Power Shell / Right-Click / Run As Administrator
      • Start-Service sshd
        # Status
        Get-Service sshd
        # OPTIONAL but recommended:
        Set-Service -Name sshd -StartupType 'Automatic'
        # Confirm the Firewall rule is configured. It should be created automatically by setup.
        Get-NetFirewallRule -Name *ssh*
        # There should be a firewall rule named "OpenSSH-Server-In-TCP", which should be enabled
        # If the firewall does not exist, create one
        New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
        
    • Access: ssh username@192.168.122.96
  13. Optional: Access host’s CD/DVD drive or ISO images from Windows_10 guest
    • Power down VM
    • In virt-manager Add Hardware: Storage
      • Device type: CDROM
  14. Implement KVM Tuning

Linux Guest

  1. See also Setup VM and Install Windows 10/11 and KVM Tuning
  2. Settings / Video / Model=Virtio, 3D acceleration when supported (Linux yes, Windows no)
  3. Kernel
    • Use linux-image-virtual-*
    • linux-image-kvm not booting

KVM Tuning

General Tuning
  1. Add <ioapic driver='kvm'/> to your <features> section
  2. Change Disk Controller from SATA to VirtIO
    1. Add New Virtual Hardware / Controller / SCSI / VirtIO SCSI
    2. Add New Virtual Hardware / Storage / VirtIO
    3. Disk controller for VirtIO after the line <controller type='scsi' ...> add “driver” line: <driver iothread='1'/>
    4. SATA Disk 1/Avanced Options/Disk bus: SATA –> VirtIO
  3. Disk performance
    • Cache mode to “writeback” for better performance (and less safety)
    • Discard mode to “unmap” for automatically shrinking the disk image when space is freed
  4. Dedicate I/O Threads
    • After the first line <domain ...>, add “iothreads” line: <iothreads>1</iothreads>
  5. Enabling multi-queue virtio-scsi and virtio-net
    • Where N is the total number of vCPUs
    • <controller type="scsi" index="0" model="virtio-scsi">
      • Add to or create “driver”: <driver queues="N"/>
      • </controller>
    • <interface type="network">
      <model type="virtio"/>
      • Add to or create driver”: <driver queues="N"/>
      • </interface>
  6. Increase the Transmit Queue Length for virt-io Interfaces
    • After VM startup: sudo /usr/local/sbin/kvm_services nettxlen
  7. CPUs
    • Host CPU Passthrough
      • Can set in virt-manager as “copy host CPU configuration”
      • XML: <cpu mode="host-passthrough"/> - Do not “Manually set CPU topology”
  8. Add timers to reduce CPU usage
    • <clock ...>
    • Add or enable
      <timer name='hpet' present='yes'/>
      <timer name='hypervclock' present='yes'/>
    • </clock>
  9. Hugepages
    • Enable automatic use of transparent hugepages until next rebooot echo 'always' > /sys/kernel/mm/transparent_hugepage/enabled
      echo 'always' > /sys/kernel/mm/transparent_hugepage/defrag - Persistent
    • Install package sysfsutils
    • File: /etc/sysfs.conf
      ###################################################################
      #
      # Enable transparent hugepages and memory defragmentation
      kernel/mm/transparent_hugepage/enabled = always
      kernel/mm/transparent_hugepage/defrag  = always
      
        - Has downsides, e.g. defragmenting RAM pages can stall the system
        - Check the amount of memory used by THP (or HugePages):    `grep -i huge /proc/meminfo`
        - THP monitoring: `egrep 'trans|thp' /proc/vmstat`
        - Check THP usage per process    `awk  '/AnonHugePages/ { if($2>4){print FILENAME " " $0; system("ps -fp " gensub(/.*\/([0-9]+).*/, "\\1", "g", FILENAME))}}' /proc/*/smaps`
        - When using static hugepages instead (pre-allocated), add the following XML
      
    • <memoryBacking> <hugepages/> </memoryBacking>
    • Not needed for transparent hugepages
Windows-Only Tuning
  1. Change Disk Controller from SATA to VirtIO after installation
    • Add VirtIO controller and new temporary VirtIO storage device as described in “Change Disk Controller from SATA to VirtIO”
      • The new disk must not be formatted, it just triggers installation of the required device drivers
      • Check in device manager or Start / Right-Click / Disk Management that the VirtIO storage device is recognized
    • Change the boot disk from SCSI to VirtIO disk
      • May need to remove and re-add it
    • Remove temporary VirtIO storage drive
  2. Enabling Hyper-V enlightenments
    Add/Modify the following <hyperv> sub-section to the <features> section of the XML:
     <features>
     [...]
       <hyperv>
         <relaxed state='on'/>
         <vapic state='on'/>
         <spinlocks state='on' retries='8191'/>
         <vendor_id state='on' value='KVM Hv'/>
         <vpindex state='on'/>
         <runtime state='on' />
         <synic state='on'/>
         <stimer state='on'>
           <direct state='on'/>
         </stimer>
         <frequencies state='on'/>
         <reset state='on'/>
         <tlbflush state='on'/>
         <reenlightenment state='on'/>
         <ipi state='on'/>
         <evmcs state='on'/>               <!-- Only for Intel! Remove for AMD processors -->
       </hyperv>
     [...]
     </features>
    
  3. Configure VirtIO Network driver parameters
    • In Windows VM Device Manager
    • Logging.Enable = Disable
    • After every update/reinstall of network driver

Configuration Files

virt-manager Usage

Virsh Command Line Management Interface

Selected domain (i.e. virtual machine) commands

Selected network commands

Selected storage commands

Selected device commands

Networking

Bridge Networking
  1. Appear as own host on network
  2. There are a lot of guides out there using older and more complicated and manual approaches
    • In 2025 you can let lbivirt do the work with the following simple steps.
    • Complete GUI usage should also be possible, confirm generated XML with the snippets below.
  3. Use a macvtap device, which directly attaches to the NIC, configuration within libvirt
    • Create multiple macvtap devices for multiple VMs
  4. Create definition, update interface for your network link name
    virsh --connect=qemu:///system net-define --validate network.xml
      <network>
         <name>macvtap0</name>
         <forward mode="bridge">
            <interface dev="enx089204252244"/>
         </forward>
      </network>
    
  5. In virt-manager add to VM / View (Hardware) Details / Directly edit XML for NIC Hardware
    <interface type="network">
      <source network="macvtap0"/>
      <model type="virtio"/>
    </interface>
    
  6. Add Libvirt/KVM interface to the firehol firewall script
    # Add Libvirt/KVM  to the World interface
    interface "... virbr+" World
       # The default policy is DROP. You can be more polite with REJECT.
       policy reject
       # The following means that this machine can REQUEST anything
       client all accept
       # ...
    
  7. Execute before VM bring-up
    virsh –connect=qemu:///system net-start macvtap0
  8. Optional cleanup after VM termination
    virsh –connect=qemu:///system net-destroy macvtap0
  9. Reference documentation
    • Use libvirt approach described above instead
    • Manual create bridge
      • ip link add dev br0 type bridge
      • ip link set dev enx089204252244 master br0
      • ip link set br0 up
      • Remove an interface from a bridge: ip link set eth1 nomaster
      • Delete a bridge: ip link delete bridge_name type bridge
    • Manual create macvtap/macvlan device
      • ip link add link enx089204252244 name macvtap0 type macvtap
      • ip link set macvtap0 up
NAT
  1. Routes all traffic over the host, which acts as DHCP server and router
    • No direct access to guest from the outside world
  2. When using firehol, allow routing to enjoy internet connection by adding the content of the file kvm_qemu/firehol-addon-nat.conf to your /etc/firehol/firehol.conf
    • It also overrides FIREHOL_RULESET_MODE to accurate", otherwise DHCP server traffic for KVM/Windows machine is blocked and no IP address can be assigned.
  3. Force IP address to 192.168.122.96 for Windows 10 machine, because thats what the firewall rules are written for
    virsh --connect=qemu:///system  net-edit default
    # Add in dhcp section, update MAC address and name accordingly
    <host mac='52:54:00:5f:ff:8e' name='win10' ip='192.168.122.96' />
    
  4. Check for Leases and addresses
    virsh --connect=qemu:///system  net-dhcp-leases default
    virsh --connect=qemu:///system  domifaddr Windows_10 --full
    
  5. Make sure forwarding is enabled
    • cat /proc/sys/net/ipv4/ip_forward # 1
  6. Host IP: 192.168.122.1 (interface virbr0)
Host-Only Network
  1. Over this network only the host and guests can communicate.
  2. Create definition, update interface for your network link name
    virsh --connect=qemu:///system net-define --validate network.xml
    <network>
     <name>hostonly</name>
     <bridge name="virbr1"/>
     <port isolated='yes'/>    <!-- Isolate guests from each other -->
     <ip address="192.168.56.1" netmask="255.255.255.0"> <!-- IP of the Host-facing bridge -->
       <dhcp>  <!-- Enable DHCP on Guest-facing network, otherwise assign static IPs by yourself -->
         <range start="192.168.56.2" end="192.168.56.254"/>
       </dhcp>
     </ip>
    </network>
    
  3. In virt-manager add new NIC to VM / View (Hardware) Details / Directly edit XML for NIC Hardware
    <interface type="network">
      <source network="hostonly"/>
      <model type="virtio"/>
    </interface>
    
  4. Show XML again, now a MAC should be assigned. Use in next step
  5. Add static IP based on MAC address assigned
    virsh --connect=qemu:///system net-edit hostonly
    Inside <dhcp> tag
       <host mac='52:54:00:5f:ff:8e' name='tutn-5tfkpn3' ip='192.168.56.3' /
    
  6. Add Libvirt/KVM interface to the firehol firewall script
    # Add Libvirt/KVM  to the World interface
    interface "... virbr+" World
       # The default policy is DROP. You can be more polite with REJECT.
       policy reject
       # The following means that this machine can REQUEST anything
       client all accept
       # ...
    
  7. Enable DHCP Support in firehol firewall script
    # Add before virbr+ World interface
    # Libvirt/KVM interface which needs special attention due to DHCP
    if [[ "$FIREHOL_USER_FEATURES" =~ ^kvm ]]; then
      interface "virbr+" VirtualDhcp
        # Continue processing after Dhcp Rules
        policy return
        # Host machine is DHCP server
        # Standard DHCP rule is not working, dunno why: server dhcp accept
        server custom virdhcp udp/67 68 accept
    fi
    
  8. Execute before VM bring-up
    virsh –connect=qemu:///system net-start hostonly
  9. Optional cleanup after VM termination
    virsh –connect=qemu:///system net-destroy hostonly

Host Device Forwarding

Dynamic USB Device Attachment

Manual add USB device

  1. In Virt-Manager VM configuration: Add Hardware / USB Host Device
  2. You can’t forward an USB hub and it’s devices.
    • If you forward the hub then still the devices plugged into it are catched by the host
  3. XML for adding USB device by vendor and product ID
    <hostdev mode='subsystem' type='usb' managed='yes'>
     <source startupPolicy='optional'>
       <vendor id='0x1234'/>
       <product id='0xbeef'/>
     </source>
     <boot order='2'/>
    </hostdev>
    
  4. XML for adding USB device by bus and device ID
    <hostdev mode='subsystem' type='usb'>
     <source startupPolicy='optional'>
       <address bus='1' device='1'/>
     </source>
    </hostdev>
    

PCI Forwarding

Files and File System Access

Reduce image file size by unsparsing and compressing contents

Resize qcow2 disk image

  1. Make a backup
  2. Delete all snapshots, see below or in virt-manager
  3. Optional “Reduce image file size by unsparsing and compressing contents” (see above)
  4. Add more disk space qemu-img resize IMAGE.qcow2 +20G
  5. Resize disk with guest tools
    • Windows: “Disk Management” utility, Right click on C:, “Extend Volume”

Convert Virtualbox VDI (or any other) disk images to QCOW2

Snapshots

Change CDROM in running system

QCow2 access

Virtiofs: Sharing files across host and guest

Further Commands and Documentation

Windows XP Notes

Direct QEMU Usage

When you’re using QEMU directly (should not be needed for this guide) here some notes.

List available machines

Invocation

Networking Guides

More monitor commands

Errors