Building Lightweight VMs with Apple's Virtualization.framework
How Virtualization.framework exposes Apple Silicon's hardware virtualization support directly to Swift applications, without a third-party hypervisor.
For years, running a VM on a Mac meant reaching for a third-party product — Parallels, VMware Fusion, or VirtualBox — each implementing its own hypervisor logic on top of lower-level platform APIs. Virtualization.framework, introduced alongside Apple Silicon support, changed that by giving developers a native, Apple-maintained API for creating and running virtual machines directly, without any third-party hypervisor engine underneath.
Built on Hypervisor.framework, exposed at a higher level
Virtualization.framework sits above the lower-level Hypervisor.framework (which exposes the CPU’s virtualization extensions much more directly, closer to how bhyve or KVM expose theirs on other platforms). Virtualization.framework’s contribution is a much higher-level, Swift/Objective-C-native API — virtual machine configuration, storage, and networking are all expressed as ordinary Swift objects rather than raw hypervisor calls.
import Virtualization
let config = VZVirtualMachineConfiguration()
config.cpuCount = 4
config.memorySize = 4 * 1024 * 1024 * 1024 // 4 GiB
Configuring boot: Linux guests and macOS guests differ
Booting a Linux guest uses VZLinuxBootLoader, pointing directly at a kernel image and initial ramdisk — no BIOS or UEFI emulation involved, since the framework boots the kernel directly, similar in spirit to how a cloud hypervisor often boots a guest kernel directly rather than emulating firmware:
let bootLoader = VZLinuxBootLoader(kernelURL: kernelURL)
bootLoader.initialRamdiskURL = initrdURL
bootLoader.commandLine = "console=hvc0 root=/dev/vda"
config.bootLoader = bootLoader
Running a macOS guest instead uses VZMacOSBootLoader along with a restore image fetched through VZMacOSRestoreImage, and requires a VZMacHardwareModel and auxiliary storage tied to the specific host — reflecting that macOS-on-macOS virtualization is a first-class, Apple-supported scenario, not a workaround.
let bootLoader = VZMacOSBootLoader()
config.bootLoader = bootLoader
config.platform = VZMacPlatformConfiguration()
Storage and networking as composable device objects
Each virtual device is its own configuration object, attached to the VM configuration before it starts — a pattern that reads similarly to assembling a bhyve command line’s -s slot,device flags, just expressed as Swift objects instead of shell arguments:
let disk = try VZDiskImageStorageDeviceAttachment(url: diskURL, readOnly: false)
let storageDevice = VZVirtioBlockDeviceConfiguration(attachment: disk)
config.storageDevices = [storageDevice]
let network = VZNATNetworkDeviceAttachment()
let networkDevice = VZVirtioNetworkDeviceConfiguration()
networkDevice.attachment = network
config.networkDevices = [networkDevice]
VZNATNetworkDeviceAttachment gives the guest outbound network access through NAT with essentially zero configuration — the framework handles the translation, much like a consumer virtualization product’s default networking mode, without needing to manually set up a bridge interface the way a bare-metal bhyve configuration would.
Starting the machine
Once fully configured, validation and startup are asynchronous operations on the VZVirtualMachine object itself:
try config.validate()
let vm = VZVirtualMachine(configuration: config)
vm.start { result in
switch result {
case .success:
print("VM started")
case .failure(let error):
print("Failed to start: \(error)")
}
}
Why this exists as a first-party framework
Apple Silicon’s transition made third-party hypervisors’ job considerably harder — the architecture change meant Parallels, VMware, and VirtualBox all needed substantial rework, and Rosetta-translated x86 guests are a fundamentally different (and slower) proposition than native ARM virtualization. Virtualization.framework gave developers (and Apple’s own tools, including elements of Xcode’s iOS Simulator infrastructure) a supported, native path to lightweight virtualization that doesn’t depend on any third party having solved Apple Silicon compatibility first — command-line tools like tart and lume, popular for CI runners that need disposable macOS or Linux VMs, are built directly on top of this framework rather than wrapping an external hypervisor.
Where it fits relative to containers and full VMs
Virtualization.framework VMs are genuinely separate kernels with hardware-enforced isolation — a meaningfully stronger boundary than a Linux container’s shared-kernel namespace isolation, at a higher resource cost than a container but a much lower one than a traditional full-emulation VM product. For CI runners needing a disposable, fully isolated macOS or Linux environment per job, or for running a specific Linux userland alongside macOS without dual-booting, it’s become the default modern answer specifically because it’s native, actively maintained by Apple, and doesn’t carry a third-party hypervisor’s own compatibility lag behind new hardware.
How it relates to the three-pillar Apple Silicon compatibility story
When Apple first announced the Mac’s transition to Apple Silicon, Craig Federighi framed compatibility around three distinct pillars: Universal apps carrying native code for both architectures, Rosetta 2 for translating existing Intel binaries, and virtualization for running genuinely different, non-macOS operating systems rather than translating individual applications. Virtualization.framework is the concrete technical realization of that third pillar — it exists specifically to let a Linux (or macOS) guest run with its own native ARM kernel, entirely separate from Rosetta’s role of translating individual x86 application binaries to run directly under the host macOS kernel. Understanding this distinction matters practically: a Linux guest running through Virtualization.framework needs a genuinely ARM-native Linux kernel and userland, not a Rosetta-translated x86 Linux distribution, since Rosetta’s translation approach and Virtualization.framework’s hardware-level isolation approach solve two fundamentally different compatibility problems rather than being interchangeable options for the same one.
Rosetta for Linux guests: bridging the two approaches
Apple later extended the framework further with Rosetta support specifically for Linux virtual machines, announced in 2022 — letting an ARM Linux guest running inside a Virtualization.framework VM itself translate and run x86 Linux binaries via a Rosetta component installed inside the guest, distinct from the host-level Rosetta 2 translation macOS itself uses for Intel macOS applications. This is a genuinely different, narrower piece of technology from host-level Rosetta 2, but it demonstrates how thoroughly Apple invested in making the virtualization path a complete answer to non-native compatibility, not just a container for running an already-ARM-native guest OS — a Linux container workflow depending on x86-specific tooling can, with this Rosetta-in-Linux-guest support enabled, still function inside an ARM Linux VM running on Apple Silicon without needing a slower, full binary-translation layer at the hypervisor level itself.
The performance ceiling this architecture actually achieves
Because Virtualization.framework exposes Apple Silicon’s own hardware virtualization extensions nearly directly (via the thin Hypervisor.framework layer underneath), a guest OS running through it executes with performance characteristics much closer to bare-metal than a software-emulated or binary-translated VM would achieve — the CPU itself is running guest instructions directly, with the hypervisor only intervening for genuinely privileged operations (I/O, certain memory management operations) that require host mediation. This hardware-assisted model is exactly why container-adjacent tools built on Virtualization.framework, like tart for CI-oriented macOS VMs, can offer meaningfully faster VM boot and execution times than older, software-virtualization-based approaches — the performance gap between “genuinely isolated VM” and “shared-kernel container” has narrowed considerably specifically because of hardware virtualization support this framework exposes directly, rather than papering over with a slower software layer.
Related:
- Apple Announces the Mac’s Transition to Apple Silicon
- The macOS Boot Process: From Firmware to the Login Window
Sources: