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.

Running a VM

Everything here works the same on a Mac and on Linux, from the shell or from the interactive interface; each section gives both.

Start and stop

$ reims-ctl start mac
$ reims-ctl list
zsh — M3 Max, macOS 27
$ reims-ctl list
NAME                 STATE      CPUS  MEMORY DISPLAYS  DIR
macos-26             running       4      8G 1x 1920x1080 ~/.local/share/reims-ctl/vms/macos-26
macos-27             stopped       4      8G 1x 1920x1080 ~/.local/share/reims-ctl/vms/macos-27

stop asks macOS to shut down, with the shortcut a Mac's keyboard sends (Control-Option-Command-Power), and powers the VM off if it hasn't by the VM's stop timeout (90 seconds unless you set another). --force powers it off at once.

$ reims-ctl stop mac
$ reims-ctl stop mac --force  # power off now
zsh — M3 Max, macOS 27
$ reims-ctl stop macos-26
macos-26: asked the guest to shut down (Control-Option-Command-Power)
macos-26: shut down

In the interface: s starts, x stops, K powers off. Closing a VM's window powers it off too. VMs keep running when you quit the interface, and the interface shows VMs started or stopped anywhere else.

The window, the keyboard and the mouse

Each display opens in a window of its own. Its title says click to capture input: click into the picture, and shortcuts and system keys go to the guest instead of the host, and the pointer stays inside the picture until you release it, so you can throw it at the guest's corners and edges.

HostWhile capturedRelease
macOS⌘ shortcuts and system keys, ⌘Q, ⌘W, ⌘Tab and ⌘Space among them⌃⌥⌘ together
Linux, on Wayland or X11the whole keyboard, so the compositor's shortcuts (Super, Alt+Tab) reach the guestCtrl+Alt+Super together

Switching to another window releases capture too, and gives the host back its keys and pointer. Keys reach the guest by position, so the guest's own keyboard layout applies, and on Linux the Super key is the guest's ⌘. Fn/Globe doesn't reach the guest, and there's no HDR output.

Make it feel native: borderless, at the host's resolution

A borderless window covers its whole screen with no title bar. On a Mac, the menu bar and Dock hide while the VM is in front and come back when another app is, and on a screen with a notch the picture fits below it. With --resolution host, the guest is offered modes that fill the screen at its density: a Retina screen gets a HiDPI desktop, a 1× screen a 1× one. Together, the guest looks like the machine's own desktop.

$ reims-ctl set macos-26 --borderless --resolution host
zsh — M3 Max, macOS 27
$ reims-ctl set macos-26 --borderless --resolution host
macos-26: saved
macos-26
  state         stopped
  kind          arm64 macOS guest (vmapple, HVF)
  directory     ~/.local/share/reims-ctl/vms/macos-26
  cpus          4
  memory        8G
  disk          disk.img (raw)
  displays      1 x host, borderless
  ecid          11400282047089759542
  audio         on
  network       user, mac 52:54:00:b7:2b:36
  logs          on (info)
  stop timeout  90 s

In the interface: e on the VM, then Borderless and Resolution. The rest of the display settings:

OptionWhat it does
--displays N1 to 8 displays, each in a window of its own.
--resolution 2560x1600The guest's first mode; host for modes that fill the host's screen. 1920×1080 by default.
--hidpiRuns the mode HiDPI: 3840×2160 looks like 1920×1080. It needs a mode at least 1920 pixels wide, and a 4K HiDPI guest runs much slower than a 1× one.
--refresh 120The refresh rate offered to the guest; the host screen's highest by default.
--env REIMS_VGPU_WINDOW_SCREENS=1,0Display i opens on the host screen the list's i-th entry names.

Change its settings

set changes any setting; it applies at the VM's next start. show prints them all.

$ reims-ctl set mac --cpus 6 --memory 12G
$ reims-ctl set mac --ssh-port 2222  # 127.0.0.1:2222 to the guest's SSH
$ reims-ctl set mac --audio false
$ reims-ctl show mac

Every option is in the command reference; --extra-args adds QEMU arguments of your own. Networking is QEMU's user-mode network, which reaches the internet from the guest; --ssh-port forwards a port on 127.0.0.1 to it, once you turn on Remote Login in macOS.

Logs

With logs on (--logs, or l in the interface), each start writes QEMU's log, with the GPU library's lines in it, and the guest's serial console into the VM's logs directory.

$ reims-ctl logs mac -n 14
$ reims-ctl logs mac --serial        # the serial console
$ reims-ctl set mac --log-level debug  # REIMS_VGPU_LOG's filter
zsh — M3 Max, macOS 27
$ reims-ctl logs macos-26 -n 14
2026-10-20T00:18:49.128226Z qemu-system-aarch64: info: apple-gfx: -display none: each display opens a window of its own; REIMS_VGPU_WINDOW=0 keeps QEMU headless
reims-vgpu INFO reims_vgpu_core::device: device starting on Metal device Apple M3 Max
reims-vgpu INFO reims_vgpu_core::device: device isolates every task but task 0
reims-vgpu INFO reims_vgpu_core::device: argument buffers on
reims-vgpu INFO reims_vgpu_core::display: display on port 0 created, serial number 0x1, name "Reims vGPU"
reims-vgpu INFO reims_vgpu_core::iosurface::device: host device 0x10e240bf0 created
reims-vgpu INFO reims_vgpu_core::device::lifecycle: device reset
reims-vgpu INFO reims_vgpu_core::guest::abi::features: guest asked for binary version 0; version 0x0 is stored
reims-vgpu INFO reims_vgpu_window::native::macos::app: the windows' application runs on the main thread
reims-vgpu INFO reims_vgpu_window::native::macos::shell: window "Reims vGPU" open: windowed
reims-vgpu INFO reims_vgpu_core::guest::abi::features: guest asked for binary version 245; version 0xf5 is stored
reims-vgpu INFO reims_vgpu_core::display::guest_display: guest display on port 0 created
reims-vgpu INFO reims_vgpu_core::display::host_calls: display on port 0: mode 1920x1080, pixel format 0x52476841, protection requirements 0x0
reims-vgpu INFO reims_vgpu_core::display::host_calls: display on port 0: mode 1920x1080, pixel format 0x52476841, protection requirements 0x0
The GPU library starting on the Mac's GPU, and the guest's driver finding its display.

In the interface, L opens the log in less: :n shows the serial console, / searches, F follows the log as it grows.

Report a bug

$ reims-ctl report mac

This zips the VM's logs, settings and QEMU command with the host's details into your Downloads folder, ready to attach to an issue. In the interface it's b. Turn logs on and reproduce the problem first, so the zip has the run in it.

Run QEMU yourself

command prints the exact QEMU command a VM runs, as a shell script, with the environment it sets. Save it with -o, change it, and run it without reims-ctl.

$ reims-ctl command macos-26 -o launch.sh
zsh — M3 Max, macOS 27
$ reims-ctl command macos-26
#!/bin/sh
# macos-26: the QEMU command reims-ctl 0.1.0 runs for this VM.
# Its window opens under -display none; QMP is at the -qmp socket.
unset REIMS_VGPU_WINDOW
export REIMS_VGPU_LOG=info
exec ~/.local/share/reims-ctl/qemu/current/bin/qemu-system-aarch64 \
  -name macos-26 \
  -accel 'hvf,kernel-irqchip=on' \
  -M 'vmapple,uuid=11400282047089759542' \
  -m 8192M \
  -smp 4 \
  -bios /System/Library/Frameworks/Virtualization.framework/Resources/AVPBooter.vmapple2.bin \
  -drive 'file=~/.local/share/reims-ctl/vms/macos-26/aux.img.trimmed,if=pflash,format=raw' \
  -drive 'file=~/.local/share/reims-ctl/vms/macos-26/disk.img,if=pflash,format=raw' \
  -drive 'file=~/.local/share/reims-ctl/vms/macos-26/aux.img.trimmed,if=none,id=aux,format=raw' \
  -drive 'file=~/.local/share/reims-ctl/vms/macos-26/disk.img,if=none,id=root,format=raw' \
  -device 'vmapple-virtio-blk-pci,variant=aux,drive=aux' \
  -device 'vmapple-virtio-blk-pci,variant=root,drive=root' \
  -netdev 'user,id=net0,ipv6=off' \
  -device 'virtio-net-pci,netdev=net0,mac=52:54:00:b7:2b:36' \
  -global 'apple-gfx-mmio.displays=1' \
  -global 'apple-gfx-mmio.resolution=1920x1080' \
  -audiodev 'coreaudio,id=snd0' \
  -device 'usb-audio,id=audio,audiodev=snd0' \
  -display none \
  -serial file:~/.local/share/reims-ctl/vms/macos-26/logs/serial.log \
  -msg 'timestamp=on' \
  -monitor none \
  -qmp 'unix:~/.local/share/reims-ctl/vms/macos-26/qmp.sock,server=on,wait=off' \
  -pidfile ~/.local/share/reims-ctl/vms/macos-26/qemu.pid
An arm64 VM's command. The GPU is apple-gfx-mmio, on Metal; on Linux it's apple-gfx-pci.

Environment overrides

Set them in QEMU's environment with reims-ctl set mac --env KEY=VALUE (and KEY= removes one). All are optional.

VariableEffect
REIMS_VGPU_LOG=infoThe library's log lines, by level and target=level pairs; --log-level sets it.
REIMS_VGPU_STATS=1A line of frame statistics a second for each display.
REIMS_VGPU_WINDOW_BORDERLESS=1Borderless windows; --borderless sets it.
REIMS_VGPU_WINDOW_SCREENS=1,0Which host screen each display opens on.
REIMS_VGPU_WINDOW_REFRESH=120The refresh rate of the modes offered to the guest.
REIMS_VGPU_WINDOW_IMPL=sdlOn a Mac, SDL3's window in place of AppKit's.
REIMS_VGPU_STRICT=1On Vulkan, end the process at each difference from Metal's answer that it would otherwise log.

Delete a VM

$ reims-ctl delete mac
$ reims-ctl delete mac --keep-files  # forget it, keep its directory
zsh — M3 Max, macOS 27
$ reims-ctl delete mac --yes
mac deleted, with ~/.local/share/reims-ctl/vms/mac