GPU-passthrough Windows 11 gaming VM. AMD RX 7900 XTX passed through to a Windows guest for Overwatch 2 with Ricochet anti-cheat.
This repo is a Cinc (open-source Chef) cookbook that configures the host (GRUB, VFIO, network bridge, libvirt VM definition, lifecycle tooling) and deploys guest setup scripts for manual execution after Windows installation.
curl -L https://omnitruck.cinc.sh/install.sh | sudo bashVerify:
cinc-client --version
cinc-solo --versionThe cookbook depends on symmetra_core and libvirt cookbooks from the
symmetra repo. To converge:
# From the symmetra repo (has all dependency cookbooks)
cd ~/Projects/symmetra/master
# Run just the overwatch cookbook
sudo cinc-client -z -o 'recipe[overwatch::default]'To uninstall:
sudo cinc-client -z -o 'recipe[overwatch::uninstall]'The cookbook ships with generic defaults. The following attributes must be overridden in a policyfile or node JSON before converging:
# Network
default['overwatch']['host_ip'] = '192.168.0.100' # host bridge IP
default['overwatch']['vm_ip'] = '192.168.0.101' # VM IP (static DHCP lease)
default['overwatch']['vm_mac'] = 'aa:bb:cc:dd:ee:ff'
default['overwatch']['physical_interface'] = 'enp8s0' # NIC for bridge
# Users
default['overwatch']['target_user'] = 'myuser' # Linux account
default['overwatch']['windows_user'] = 'myuser' # Windows guest account
# SMBIOS — must match real hardware for anti-cheat
default['overwatch']['smbios'] = {
'bios_vendor' => 'American Megatrends International, LLC.',
'bios_version' => '1.00',
'bios_date' => '01/01/2025',
'sys_manufacturer' => 'My Vendor',
'sys_product' => 'My Product',
'sys_version' => '1.0',
'board_manufacturer' => 'My Vendor',
'board_product' => 'My Board',
'board_version' => '1.0',
'board_serial' => 'XXXXXXXXXX', # your board serial
'sys_serial' => 'To be filled by O.E.M.',
}
# USB devices to pass through to the VM
default['overwatch']['usb_devices'] = [
{ 'vid' => '0x1234', 'pid' => '0x5678', 'name' => 'My Keyboard' },
]Read your host's SMBIOS values with sudo dmidecode -t bios -t system -t baseboard.
These attributes have sensible defaults but may need adjustment per host:
| Attribute | Default | Notes |
|---|---|---|
gpu |
0000:03:00.0 |
dGPU PCI address (lspci -D | grep VGA) |
gpu_audio |
0000:03:00.1 |
GPU HDMI/DP audio function |
host_cpus |
0-1 |
CPUs reserved for host (not pinned to VM) |
emulator_cpuset |
0-1 |
CPUs for QEMU emulator threads |
vcpu_pins |
cores 2-7 | vCPU-to-physical-core pinning map |
vm_ram_kib |
50331648 (48G) | VM RAM in KiB |
vm_vcpus |
6 | Number of vCPUs |
grub_cmdline_common |
IOMMU + AVIC | Kernel params shared by both modes |
grub_cmdline_host_mode |
overwatch.mode=host |
host-mode-only kernel params |
grub_cmdline_vm_mode |
vfio-pci.ids + hugepages + isolcpus | vm-mode-only kernel params |
host_mode_services |
['ollama.service'] |
Units gated to host-mode boots |
virtio_iso |
(empty) | Path to virtio-win.iso for driver install |
libvirtcookbook (base packages, libvirtd service)- GPU ROM (
/usr/share/qemu/gpu-rom.bin) — download the VBIOS for your specific GPU from TechPowerUp VGA BIOS Collection. The cookbook logs a warning if the file is missing but does not fail. - VirtIO ISO (
~/Downloads/virtio-win.iso) — optional; mounted as a CDROM if present during VM definition for driver installation.
- Run guest configuration automatically. The guest must be running
with QEMU guest agent installed before
setup-guest.shcan execute. - Install Windows. This is manual;
autounattend.xmlis deployed to assist unattended installs. - Download the GPU ROM. This is specific to the GPU model and must be placed manually.
- Apply netplan. The bridge config is written but
netplan applyis not called — this is dangerous over SSH and could sever the connection. A warning is logged instead.
After the cookbook converges:
- Reboot to pick up GRUB IOMMU and VFIO module changes.
- Apply netplan (if first run):
sudo netplan apply - Install Windows into the VM:
- Attach a Windows ISO to the VM definition.
virsh start overwatchand connect via VNC/Spice for initial setup.- Install VirtIO drivers from the mounted ISO.
- Install QEMU guest agent.
- Run guest setup:
sudo /usr/local/share/overwatch/setup-guest.sh allfrom the host. This configures power settings, removes HDA audio, disables Defender telemetry, sets up the shutdown signal listener, and tunes display/performance settings inside the guest.
Always use systemd:
sudo systemctl start overwatch
sudo systemctl stop overwatchNever use these directly:
virsh destroy— yanks GPU mid-operation, corrupts GPU statevirsh reboot— causes grey screen / TDR with GPU passthroughvirsh shutdown— bypasses cleanup sequencesudo overwatch start— must go through systemdvirsh undefine --nvram— destroys UEFI NVRAM. To update XML, usevirsh define <file>.
Before modifying VM XML or NVRAM:
sudo cp /var/lib/libvirt/qemu/nvram/overwatch_VARS.fd{,.bak}Before risky guest operations (no qcow2 snapshots exist by default):
sudo virsh snapshot-create-as overwatch --name <label> --disk-onlyHost and guest configs must stay in sync. A change that prevents the VM from booting risks a host kernel panic — a failed boot leaves the GPU held via vfio with no guest to release it.
- Wait 30-60s — AMD GPU driver init can take this long. It often recovers.
- Check guest agent:
sudo virsh qemu-agent-command overwatch '{"execute":"guest-ping"}' - If the agent responds, Windows is running — wait longer for the display.
- Only if unrecoverable:
sudo systemctl stop overwatch - Host reboot only needed on kernel panic (green screen).
- After forced stop, check
sudo dmesg | grep -i amdgpubefore restarting.
The agent runs as SYSTEM. Commands via guest-exec run in the SYSTEM
session, not the interactive user session:
- GUI apps are invisible to the logged-in user
HKCU:maps to SYSTEM's hive, not the user's- Use
HKU\<SID>for per-user registry access
Windows Defender and Tamper Protection must always remain on.
The lifecycle script (files/overwatch.sh) and the monitors library
(files/overwatch-monitors.sh) are static cookbook_files — fully
verbatim bash, no per-host substitution. Per-VM values are loaded at
runtime by sourcing /usr/local/share/overwatch/<vm>/instance.conf,
which is rendered from templates/instance.conf.erb and carries
identity, host topology, listener ports, and capability toggles for the
running instance.
The guest setup script (templates/setup-guest.sh.erb) is a per-VM
template since it bakes guest-side ports + the Windows user into a
script that gets pushed into the VM.
The VM XML (templates/overwatch-vm.xml.erb) is fully generated from
attributes.
The VM definition includes five vectors to avoid hypervisor detection by anti-cheat software:
- KVM hidden — hides CPUID leaf
0x40000000 - Hyper-V vendor ID — spoofs to
AuthenticAMD(avoidsMicrosoft Hv) - CPU host-passthrough — exposes the real CPU model
- SMBIOS strings — real motherboard vendor/product/version from host
- GPU VFIO passthrough — real GPU, not emulated
Mode switching is reboot-based via two custom GRUB menuentries
(overwatch-host-mode, overwatch-vm-mode), rendered into
/etc/grub.d/40_overwatch_modes. The kernel cmdline is split across
three attributes:
grub_cmdline_common— shared by both modes:amd_iommu=on— enable IOMMUiommu=pt— passthrough mode (only VFIO devices use IOMMU)kvm_amd.avic=1,kvm.ignore_msrs=1,kvm.report_ignored_msrs=0
grub_cmdline_host_mode— host-mode only:overwatch.mode=host— runtime mode marker (read by launcher + drop-ins)
grub_cmdline_vm_mode— vm-mode only:overwatch.mode=vmvfio-pci.ids=...— claim dGPU + audio at boot, no in-uptime rebindvfio-pci.disable_vga=1hugepages=24576— 48GiB of 2MB huge pages for VM memoryisolcpus=domain,managed_irq,2-7— isolate vCPU cores from host schedulernohz_full=2-7— disable periodic timer tick on vCPU coresrcu_nocbs=2-7— move RCU callbacks off vCPU cores
The active mode is persisted in grubenv (saved_entry) and switched by
/usr/local/bin/overwatch-mode require {host|vm <name>}, which sets the
saved entry and schedules a reboot. After the reboot,
overwatch-resume.service reads /proc/cmdline and the
/run/overwatch/pending-vm marker, then either starts the requested
overwatch@<vm>.service (vm-mode) or starts each unit in
host_mode_services (host-mode).
An IVRS ACPI override (/boot/ivrs-override.img via
GRUB_EARLY_INITRD_LINUX_CUSTOM) patches the MSI X870E firmware's
broken IOMMU exclusion flags that block VFIO.
virsh dominfo overwatch # VM is defined
systemctl cat overwatch # service unit installed
/usr/local/bin/overwatch status # host-ready report
ls /usr/local/bin/overwatch* # lifecycle script + monitors
ls /usr/local/share/overwatch/ # guest setup + autounattend availableInSpec: cinc-auditor exec compliance/profiles/default
reference.md— Known problems, debugging checklists, stress tests