Gaming in a Windows VM with NixOS

Posted on April 15, 2025

I want to play video games in a Windows VM with native hardware level speeds on a Linux host. What is needed to accomplish this is PCI passthrough via OVMF with QEMU. Using NixOS as the host machine makes the configuration declarative and centralized. As lots of components have to be configured, not having to edit several files makes this process a lot easier to manage as well as being able to undo any changes.

This is not intended to be a complete step through guide but instead serves as a minimally complete example of configuration required in Nix that is hopefully enough to get you started. For a full in-depth explanation and configuration options, I highly recommend looking at the Arch Linux wiki page first before reading on.

Below is my vfio.nix module that I add to my list of imports in my system level configuration.nix

{ pkgs, ... }:

{
  # https://wiki.archlinux.org/title/PCI_passthrough_via_OVMF#Loading_vfio-pci_early
  boot.initrd.kernelModules = [
    "vfio_pci"
    "vfio"
    "vfio_iommu_type1"

    "i915" # Always make sure any graphics early kernel modesetting are after vfio modules
  ];

  # I'm not sure if it is important to make sure the the passthrough GPU doesn't get loaded on boot but it might save some headache.
  boot.kernelParams = [
    "radeon.runpm=0,"
    "radeon.modeset=0,"
    "amdgpu.runpm=0,"
    "amdgpu.modeset=0,"
    "intel_iommu=on" # use amd_iommu=on if on an AMD platform
    "vfio-pci.ids=1002:67b0,1002:aac8,144d:a80a" # Read the Arch Wiki to see how to find your GPU PCI IDs.
  ];

  boot.blacklistedKernelModules = [
    "amdgpu"
    "radeon"
  ];

  hardware.graphics.enable = true;
  virtualisation.spiceUSBRedirection.enable = true;

  # Add your user to the libvirtd group
  users.extraUsers.USERNAME.extraGroups = [ "libvirtd" ];

  # Sets up virtualization services
  programs.virt-manager.enable = true;
  virtualisation.libvirtd = {
    enable = true;
    qemu = {
      package = pkgs.qemu_kvm;
      runAsRoot = true;
      swtpm.enable = true;
      ovmf = {
        enable = true;
        packages = [(pkgs.OVMF.override {
          secureBoot = true;
          tpmSupport = true;
        }).fd];
      };
    };
  };

  environment.systemPackages = [
    pkgs.OVMF
    pkgs.qemu
    pkgs.dnsmasq
    pkgs.edk2
    (pkgs.writeShellScriptBin "qemu-system-x86_64-uefi" ''
      qemu-system-x86_64 \
      -bios ${pkgs.OVMF.fd}/FV/OVMF.fd \
      "$@"
    '')
  ];
}

After configuration NixOS to support gpu passthrough, it will be time to configure the VM inside QEMU. Refer to the Arch Linux wiki https://wiki.archlinux.org/title/PCI_passthrough_via_OVMF#Setting_up_an_OVMF-based_guest_virtual_machine