Using VRChat in a Virtual Machine


Advanced Users Only!

This information is generally only useful for advanced users and specifically only those running VRChat in a virtual machine. It will contain language that may not be recognizable or useful for most users.


Running VRChat in VMs is Unsupported!

Using VRChat via a Virtual Machine is not directly supported, however we have done some research to try to make things easier for those that choose to try anyways.

The presence of this documentation does not mean we support running VRChat in a VM. It is possible that these methods or any others may stop working at any given time. We will try to keep this documentation up to date regardless.

VRChat's anti-cheat solution, Easy Anti-Cheat (EAC) generally fails when running in a Virtual Machine (VM) environment.

This can be very easily worked around though. Most notably, working around it does not have a performance impact.


Use virsh edit VM_NAME to open the VM's XML config in your favorite editor. Then simply add the following line under features > hyperv:

<vendor_id state='on' value='0123756792CD'/>

In the end, your features block should look something like this:

    <hyperv mode='custom'>
      <!-- other things here -->
      <vendor_id state='on' value='0123756792CD'/>
    <!-- other things here -->

Again, this does not affect performance. You can leave other settings, such as the hypervisor, topoext and invtsc cpuid flags or the hyper-v clock (which all do have performance benefits) enabled.

In fact, here's a little trick: Instead of just adding a vendor_id, you can set the Hyper-V mode to passthrough:

<hyperv mode='passthrough'>
  <!-- whatever -->

This will enable all available hyper-v enlightenments* that are available for your kernel/QEMU version - including a vendor id, meaning EAC will not complain, but also including other features you might not have had enabled before, meaning this may very well improve your performance!

* "enlightenments" is hyper-v's way of saying "paravirtualized extensions", i.e. interfaces that the linux kernel or QEMU provide to windows guests to enhance performance or functionality in virtual environments

After 26 AUG 2022, the above fixes may not be enough to get the game to run still (you should still do them). If you are still facing issues, try manually setting SMBIOS strings for your virtual machine. Theoretically, any valid hardware configuration should work fine, but your best bet would be to pull your own system's baseboard information via dmidecode.

You should be able to extrapolate info about your baseboard from these commands:

dmidecode --type bios
dmidecode --type baseboard
dmidecode --type system

To do so, add this to your domain root but preferably use your system's information:

  <sysinfo type="smbios">
      <entry name="vendor">American Megatrends Inc.</entry>
      <entry name="version">F31o</entry>
      <entry name="date">12/03/2020</entry>
      <entry name="manufacturer">Gigabyte Technology Co., Ltd.</entry>
      <entry name="product">X570 AORUS ULTRA</entry>
      <entry name="version">x.x</entry>
      <entry name="serial">BASEBOARD SERIAL HERE (or "Default string")</entry>
      <entry name="uuid">BASEBOARD UUID HERE</entry>
      <entry name="sku">BASEBOARD SKU HERE (or "Default string"</entry>
      <entry name="family">X570 MB</entry>

For your UUID entry, you can use the VM's generated UUID at the top of your domain XML. The UUID in your domain XML and in your sysinfo will need to match, otherwise libvirt will complain.

Also, add this under your <os> group to make use of those new system parameters:

    <smbios mode="sysinfo"/>
    <!-- whatever -->


If you're starting your VM using a raw QEMU command line, you just have to add the vendor id to the -cpu flag. For example:

-cpu host,migratable=off,hypervisor=on,invtsc=on,hv-time=on,hv-vendor-id=0123756792CD

There may be other stuff in your -cpu argument already, in which case you just append ,hv-vendor-id=0123756792CD to the end, as shown above.

You can use hyper-v passthrough mode here too:

-cpu host,migratable=off,hypervisor=on,invtsc=on,hv-time=on,hv-passthrough=on

(note: you do not need any other hv-foo arguments other than (I think?) hv-time in that case, passthrough will take care of the rest)

(further note: you'll have to figure out how to apply the os/smbios options from the libvirt section yourself if you are indeed using QEMU, since I was using libvirt when investigating a fix)


Make sure your operating system is set to "Windows 7" or higher in the "Options" tab of your VM. No other action should be required.

(note: same deal as above, this may or may not be enough to get the game to work since the 26 AUG 2022 update broke things)

Technical notes

You may notice that some of this is very similar to the infamous "NVIDIA code 43 fix" from the past. Really the only difference here is that hiding the kvm interface (kvm=off or <kvm><hidden state='on'/></kvm>) is not even required (but also doesn't hurt). If you have previously set up your VM using such a guide (e.g., then VRC with EAC should be working out of the box for you.

On a technical level, what the hyper-v vendor id does is setting leaf 0x40000000 of the guest's cpuid information to whatever is provided. The default here is "Microsoft HV", which EAC simply denies. Using hyper-v passthrough mode, this turns into "Linux KVM Hv", which still indicates a VM, but EAC is unfazed by it.

Since this does not require changing the hypervisor flag, the OS kernel in the guest (Windows NT) will still recognize the environment as a VM and apply performance enhancing measures as such. This also means that the Task Manager in the guest will report running in a virtual machine. This does not matter for EAC in testing within a KVM environment.

Very technical notes

Here's a little win c++ program to demonstrate the effect of the changes:

#include <iostream>
#include <intrin.h>

void print_leaf(int leaf)
    int res[4];
    __cpuid(res, leaf);

    std::cout << "leaf: 0x" << std::hex << leaf << std::endl;

    for (size_t i = 0; i < 4; i++)
        std::cout << "res" << i << ": 0x" << std::hex << res[i] << " (";
        for (size_t j = 0; j < 4; j++)
            char part = (res[i] >> j * 8) & 0xff;
            std::cout << part;
        std::cout << ")" << std::endl;

int main()
    // cf:

    std::cout << "manufacturer id:" << std::endl;
    print_leaf(0); // Manufacturer ID

    std::cout << "hyper-v id:" << std::endl;
    print_leaf(0x40000000); // Hypervisor CPUID Leaf Range

    return 0;

Output on an example machine using hv-passthrough:

manufacturer id:
leaf: 0x0
res0: 0x10 (►   )
res1: 0x68747541 (Auth)
res2: 0x444d4163 (cAMD)
res3: 0x69746e65 (enti)
hyper-v id:
leaf: 0x40000000
res0: 0x40000005 (♣  @)
res1: 0x756e694c (Linu)
res2: 0x564b2078 (x KV)
res3: 0x7648204d (M Hv)