What a virtual machine actually is
A virtual machine is a complete, isolated copy of a computer running as software inside another computer. Not a sandboxed process, not a chroot, not a container sharing the host's kernel — a VM boots its own kernel, manages its own memory, and runs entirely unaware it isn't real hardware. The software that makes this possible, the hypervisor, sits between the guest OS and the physical CPU, intercepting the handful of privileged operations a guest can't be allowed to do directly (touching real physical memory it doesn't own, talking to real hardware devices) and letting everything else run at full speed.
That "everything else runs at full speed" part is the entire point, and it's also the part people most often get wrong when they haven't looked closely at how virtualization actually works on this hardware.
Hardware virtualization, not emulation
These two get conflated constantly, and the distinction matters for performance:
- Emulation translates every instruction from one CPU architecture to another in software — an x86 emulator running on ARM has to decode and re-implement x86 instructions one at a time. It works for anything, and it's slow, because every single instruction pays a translation tax.
- Hardware virtualization runs the guest's instructions directly on the real CPU, at full native speed, and only traps to the hypervisor for the small set of operations that need arbitration — memory mapping, interrupts, device access. The guest and the host are the same architecture; the CPU itself has dedicated silicon for keeping them safely apart.
A Linux VM on an Apple Silicon Mac is the second kind. ARM64 guest, ARM64 host, ARM64 silicon underneath — there's no instruction translation happening for the VM itself. (Rosetta is a separate, narrower thing layered on top for running x86_64 binaries inside an ARM64 Linux guest — not what makes the VM itself work. More on that below.)
What Virtualization.framework actually gives you
Apple's Virtualization.framework is the high-level API for creating and managing VMs on Apple Silicon — the layer Velo Workspaces and most other VM apps on macOS are built on. A VZVirtualMachineConfiguration is where you declare what a VM gets: how much CPU and memory, a boot loader, and a list of devices. Those devices follow the VIRTIO specification — a standard set of paravirtualized interfaces for network, storage, socket, serial, entropy, and memory-balloon devices, so the guest talks to an efficient purpose-built interface instead of the hypervisor pretending to be a specific real piece of hardware. (The full mechanics of why paravirtualization is fast — and where it isn't the whole story — are in the performance deep dive; this page stays at the "what exists" level. For the actual device inventory — boot loaders, storage, networking, graphics, shared folders, and the rest — see how Virtualization.framework works, device by device.)
Linux guests and macOS guests aren't the same offer
Virtualization.framework supports two kinds of guests, and they come with different rules:
| Linux guest | macOS guest | |
|---|---|---|
| Runs on | Apple Silicon and Intel Macs | Apple Silicon Macs only |
| Image source | Official distro installer ISO | Official IPSW restore image, downloaded directly from Apple |
| x86_64 binary support | Rosetta for Linux, if the guest opts in | Not applicable — macOS guests only run Apple-silicon macOS |
| GPU acceleration | None — paravirtualized 2D framebuffer only | Real Metal-accelerated graphics |
That last row surprises people. A macOS guest gets genuine hardware-accelerated graphics through Virtualization.framework's Mac-specific graphics device. A Linux guest doesn't — more on why below. See what's specifically different about Ubuntu, or the full rundown of what a macOS guest actually gets that a Linux one doesn't.
CPU and memory: hardware-assisted, not simulated
Under the hood, Apple's Hypervisor framework gives Virtualization.framework access to ARM's actual virtualization extensions — a dedicated CPU exception level for the hypervisor (EL2), and a second stage of memory translation so the guest can manage its own page tables while the host still controls what physical memory it can really touch. None of that is simulated in software; it's silicon built for exactly this. That's the short version — the full explanation of why it doesn't cost what you'd expect, with the actual numbers, is in the ARM virtualization performance deep dive.
Where the GPU story is different — and why AI Bridge exists
This is the one real limitation worth knowing up front: Virtualization.framework gives a Linux guest virtio-gpu, a paravirtualized 2D framebuffer, with no path to the host's actual GPU — confirmed directly by Apple's own container team in public discussion. It's a deliberate limitation, not a bug: there's no SR-IOV-equivalent standard yet for slicing up a GPU this way on Apple Silicon. That's a real constraint if what you want is a Linux VM doing GPU compute directly inside the guest.
It's also exactly why AI Bridge works the way it does, rather than trying to solve a problem the platform doesn't support: the model server stays on the host, on the real GPU, and the guest reaches it over a lightweight paravirtualized socket channel instead. See the full architecture and the complete guide to running AI agents this way.
Where VM overhead actually comes from
Four things, and only four: CPU trap cost, memory translation overhead, interrupt delivery, and I/O device access. Apple Silicon's hardware virtualization extensions make the first three cheap by design — dedicated exception level, TLB-cached two-stage translation, hardware-assisted interrupt delivery. The fourth, I/O, is where the paravirtualization-vs-passthrough tradeoff above actually shows up in practice. None of this is asserted here — it's measured, with real numbers, in the performance deep dive and in the inference benchmark this blog has published.
When a VM is actually the right tool
A VM makes sense when you need a genuinely separate kernel — a different OS entirely, a security boundary a shared-kernel container can't give you, or a clean, disposable environment you can throw away and recreate identically. It's the wrong tool when a container would do: if the workload just needs dependency isolation on the same Linux kernel, a container starts faster and uses less memory, and there's no reason to pay for a second kernel you don't need. That tradeoff — and where Docker Desktop, OrbStack, and a real disposable VM actually sit on it — is covered in do you need a VM, or is a container enough?
Related reading: what actually determines VM performance on ARM, the architecture behind AI Bridge, and the complete guide to running AI agents this way. Or download Velo Workspaces and try it yourself.