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$ 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-27stop 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$ reims-ctl stop macos-26
macos-26: asked the guest to shut down (Control-Option-Command-Power)
macos-26: shut downIn 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.
| Host | While captured | Release |
|---|---|---|
| macOS | ⌘ shortcuts and system keys, ⌘Q, ⌘W, ⌘Tab and ⌘Space among them | ⌃⌥⌘ together |
| Linux, on Wayland or X11 | the whole keyboard, so the compositor's shortcuts (Super, Alt+Tab) reach the guest | Ctrl+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$ 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 sIn the interface: e on the VM, then Borderless and Resolution. The rest of the display settings:
| Option | What it does |
|---|---|
--displays N | 1 to 8 displays, each in a window of its own. |
--resolution 2560x1600 | The guest's first mode; host for modes that fill the host's screen. 1920×1080 by default. |
--hidpi | Runs 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 120 | The refresh rate offered to the guest; the host screen's highest by default. |
--env REIMS_VGPU_WINDOW_SCREENS=1,0 | Display 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 macEvery 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$ 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 0x0In 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 macThis 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$ 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.pidapple-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.
| Variable | Effect |
|---|---|
REIMS_VGPU_LOG=info | The library's log lines, by level and target=level pairs; --log-level sets it. |
REIMS_VGPU_STATS=1 | A line of frame statistics a second for each display. |
REIMS_VGPU_WINDOW_BORDERLESS=1 | Borderless windows; --borderless sets it. |
REIMS_VGPU_WINDOW_SCREENS=1,0 | Which host screen each display opens on. |
REIMS_VGPU_WINDOW_REFRESH=120 | The refresh rate of the modes offered to the guest. |
REIMS_VGPU_WINDOW_IMPL=sdl | On a Mac, SDL3's window in place of AppKit's. |
REIMS_VGPU_STRICT=1 | On 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$ reims-ctl delete mac --yes
mac deleted, with ~/.local/share/reims-ctl/vms/mac