Beta

How well it runs can still depend on your host and its GPU. Metal, macOS are trademarks of Apple Inc. Reims vGPU is an independent project and is not affiliated with, sponsored by, or endorsed by Apple Inc.

A macOS VM on x86-64 Linux

On x86-64 Linux, a VM runs x86-64 macOS on QEMU's q35 machine under KVM, booted through OpenCore, with its GPU on Vulkan through metal2vulkan. macOS 26 Tahoe is the newest release, and the last for Intel Macs. reims-ctl downloads the installer from Apple and makes the VM's OpenCore image; you install macOS from Recovery, as on a Mac, then set one preference in the guest.

Prepare the host

  • KVM. /dev/kvm must be usable by you: virtualization on in the firmware, and you in the kvm group (sudo usermod -aG kvm $USER, then log in again).
  • A Vulkan driver for your GPU. reims-ctl gpus lists the GPUs a VM can be given; by default it takes the first discrete GPU, then the first integrated one.
  • A higher vm.max_map_count. The library's views of guest memory take a kernel mapping for each run of guest pages, about 64 per MiB, so the stock limit of 65,530 is far too low. 2,097,152 covers a guest of up to 30 GiB. reims-ctl and the installer warn when it's too low.
  • On an AMD GPU, guest memory reaches the GPU through /dev/udmabuf, so you need to be in the group that owns it: kvm on Ubuntu, the same group as /dev/kvm.
  • A desktop session, Wayland or X11, for the VM's windows. Over SSH, the terminal viewer shows the screen instead.
$ sudo sysctl -w vm.max_map_count=2097152
$ echo vm.max_map_count=2097152 | sudo tee /etc/sysctl.d/90-reims-vgpu.conf  # keep it
$ reims-ctl gpus
zsh — Linux, RTX 5080 laptop
$ reims-ctl gpus
By default the library takes the first discrete GPU, then the first integrated one.
 1. NVIDIA GeForce RTX 5080 Laptop GPU  [10de:2c19]  /usr/share/vulkan/icd.d/nvidia_icd.json
 2. llvmpipe (LLVM 23.1.1, 256 bits)  [10005:0000]  /usr/share/vulkan/icd.d/lvp_icd.json
Give a VM one with: reims-ctl set NAME --gpu NUMBER

reims-ctl doctor checks all of it; Install shows its output on this laptop.

Create the VM

$ reims-ctl create mac --macos tahoe

reims-ctl asks Apple for Tahoe's recovery image, about 1 GB, and makes the VM's OpenCore image, its firmware and a new 128G disk. The OpenCore image presents the VM as a Mac with an identity of its own. Downloads are kept in ~/.local/share/reims-ctl/installers for your next VM, and an interrupted one picks up where it stopped. With the image already there, the create took 6 seconds:

zsh — Linux, RTX 5080 laptop
$ reims-ctl create mac --macos tahoe
mac: asking Apple for the macOS Tahoe 26 recovery image
mac: Apple's product 140-93589
mac: checking what is already downloaded
mac: checking what is already downloaded: 1% of 961 MB
mac: checking what is already downloaded: 100% of 961 MB
mac: ~/.local/share/reims-ctl/installers/macOS-Tahoe-online-installer.dmg is already here and checks out
mac: creating the disk
mac: building the OpenCore 1.0.8 image
mac: Wrote ~/.local/share/reims-ctl/vms/mac/opencore.qcow2
mac: writing OVMF's code and variables
mac: Wrote ~/.local/share/reims-ctl/vms/mac/OVMF_CODE.fd and ~/.local/share/reims-ctl/vms/mac/OVMF_VARS.fd
mac: OpenCore presents the VM as an iMac20,1, serial C02•••••••••
mac: converting macOS-Tahoe-online-installer.dmg to a raw image
mac: done
created mac in ~/.local/share/reims-ctl/vms/mac
To install macOS, start mac: OpenCore boots its installer into macOS Recovery. If the VM's disk is new, open Disk Utility, erase it (QEMU HARDDISK Media) as APFS and quit Disk Utility. Then pick Reinstall macOS. Once macOS is installed, eject the installer (reims-ctl eject mac) and set Metal's error mode (README).
Real output. The serial number OpenCore generated is masked here; it's yours to keep private.

Other ways to get macOS:

OptionWhat it does
--macos tahoeApple's recovery image for that release: a name (tahoe), a number (26) or latest. It downloads the newest build as it installs.
--macos 26.6.2 --offlineOne exact release (or a build, such as 25G83), 13 to 20 GB, which installs with no network.
--installer FILEAn installer of your own, such as a BaseSystem.dmg; a DMG is converted to a raw image in the VM's directory.
--disk FILE --opencore FILEA disk that already has macOS, copied (a reflink where the file system can, or --disk-mode in-place), with the OpenCore image it booted from, OVMF_CODE.fd and OVMF_VARS.fd beside it. It keeps its Mac's identity.

In the interface, n opens the same create as a form, with a list of the macOS versions to pick from.

Install macOS

  1. reims-ctl start mac. OpenCore boots the installer into macOS Recovery.
  2. Open Disk Utility, select the VM's disk (QEMU HARDDISK Media), erase it as APFS, and quit Disk Utility.
  3. Pick Reinstall macOS. With an offline installer, open Utilities › Terminal and run sh /Volumes/Installer/install-macos.sh instead.
  4. Once macOS is installed, take the installer out:
$ reims-ctl eject mac

To get back the space installers take, delete them from ~/.local/share/reims-ctl/installers once your VMs' installers are ejected.

Set Metal's error mode, once

An x86-64 guest needs one setting before its desktop works. Without it, macOS's icon service crashes as soon as it starts, and a fresh install never gets a working desktop: it stays on its wallpaper with no menu bar and no Dock, or comes up gray with a menu bar but no Dock. Since you can't count on a desktop yet, set it from macOS Recovery:

  1. Turn on the VM's boot selection, so OpenCore shows its picker: in the interface, e on the VM, switch Boot selection on and save, or run:
    $ reims-ctl update-opencore mac --boot-selection
    It's off by default, and then the VM boots straight into macOS. (If your update-opencore doesn't know --boot-selection, your reims-ctl predates it and the picker is always on: skip this step.)
  2. Start the VM and pick the Recovery entry in OpenCore's picker before it boots macOS. It waits 5 seconds; any arrow key stops the countdown, and Space shows the entry if it's hidden.
  3. In Recovery, open Utilities › Terminal and type this as one line:
    $ cd /Volumes/*Data/Library/Preferences && defaults write "$PWD/.GlobalPreferences" METAL_ERROR_MODE -int 5 && chmod 644 .GlobalPreferences.plist && reboot

The VM restarts only if every step worked. If it doesn't, read the error: the setting wasn't saved, and macOS keeps crashing until it is. Don't drop the chmod; without it, macOS can't read the setting. Then pick macOS in the picker, and the desktop comes up. Turn boot selection off again the same way if you like.

If you already have a desktop, run this in its Terminal instead, then restart:

$ sudo defaults write /Library/Preferences/.GlobalPreferences METAL_ERROR_MODE -int 5

If a Dock icon stays blank afterwards, clear the Dock's icon cache:

$ rm -rf "$(getconf DARWIN_USER_CACHE_DIR)com.apple.dock.iconcache"; killall Dock

Keep the OpenCore image current

A VM's OpenCore image comes from the reims-util that made it, and a newer reims-util's image carries more. Notably, an older one lacks ReimsVgpu.kext, which macOS 26 needs for a display wider than 2048 pixels and for GPU-accelerated Chrome; the VM's details in the interface say when it's missing. update-opencore makes a new image presenting the same Mac, its model, serial number, MLB, UUID and ROM read from the old one, so macOS sees no new machine. The VM keeps its OVMF variables, which hold its boot entries, and its boot selection. Anything else you changed in the old image isn't carried over.

$ reims-ctl stop mac
$ reims-ctl update-opencore mac  # or U on the VM in the interface

reims-ctl update installs the reims-util this reims-ctl needs, so run it first if you haven't lately.

What a VM looks like

zsh — Linux, RTX 5080 laptop
$ reims-ctl show macos-26
macos-26
  state         running (pid 81348)
  kind          x86-64 macOS guest (q35 and OpenCore, KVM)
  directory     ~/.local/share/reims-ctl/vms/macos-26
  cpus          8
  memory        16G
  disk          disk.qcow2 (qcow2)
  displays      1 x 3840x2400 HiDPI (looks like 1920x1200), borderless
  gpu           NVIDIA GeForce RTX 5080 Laptop GPU
  opencore      ~/.local/share/reims-ctl/vms/macos-26/opencore.qcow2
  installer     none
  cpu model     Skylake-Client-noTSX-IBRS
  audio         on
  network       user, mac 52:54:00:ed:2a:d9, ssh on 127.0.0.1:2222
  logs          on (info)
  stop timeout  90 s
The macOS 26 VM the project tests on: a 3840×2400 HiDPI display, borderless, on the laptop's RTX 5080, with SSH forwarded from port 2222.

Options worth knowing

  • --gpu picks the GPU: auto, a number or name from reims-ctl gpus, or a Vulkan ICD JSON file.
  • --ssh-port 2222 forwards port 2222 on 127.0.0.1 to the guest's SSH, once you turn on Remote Login in macOS.
  • reims-ctl set mac --graphics vmware-svga runs the VM on QEMU's VMware SVGA instead, one display with no GPU acceleration in QEMU's own window: a fallback for telling a host problem from a guest one. --graphics reims-vgpu switches back.
  • Audio is on by default, as a USB audio device on the host's sound. Keep it on: without an output device, Safari's media players, YouTube's among them, fail.

Known differences on x86-64

  • Finder shows a file's type icon instead of a thumbnail for images and videos: macOS's thumbnail extensions end when they first look for Metal's devices, and no setting in the guest avoids it.
  • VMs have no ACPI sleep (a guest woken from S3 loses its display). macOS turns the display off instead, and input through the window turns it back on.