Introducing pvmlab: Your Local PXEboot Virtual Lab
I’m excited to share a new project I’ve been working on: pvmlab.
pvmlab is a CLI tool designed to automate the setup of a simple, reproducible virtual pxeboot provisioning lab directly on macOS. It leverages the power of QEMU, socket_vmnet (a daemon from the Lima VM project that exposes Apple’s vmnet.framework for rootless virtual networking), cloud-init, and Docker to create a contained environment where you can experiment with network booting and bare-metal provisioning simulations without needing actual hardware.
Why pvmlab?
The genesis of this project traces back to my summer “funemployed” period, I spent some time doing some personal research into macOS virtualization, virtual networking, and lightweight Docker and Kubernetes environments. My initial goal was to create a cost-effective virtual lab for learning Kubernetes, avoiding the significant investment required for a physical home lab.
Shortly after, I joined Crusoe, the Company has asked me to help re-architect their provisioning stack.
Faced with limited access to a physical hardware lab for development, I realized that my earlier summer prototype could be evolved into a robust solution to unblock my work. Over the course of a month, I developed the tool to orchestrate a multi-VM lab on a single laptop. This included a proof-of-concept PXE boot stack featuring a Dockerized DHCP/TFTP infrastructure and a minimal 8MB initrd with a custom OS installer.
Although the project focus at work eventually shifted, I recognized the value of the tool and continued to develop and refine pvmlab independently in my spare time.
Setting up a lab to test PXE booting, DHCP configurations, or OS installers often involves a complex web of VirtualBox bridges, manual network configurations, or dedicated hardware. pvmlab aims to streamline this by providing:
- Isolation: Uses
socket_vmnetto create a private, high-performance virtual network. - Architecture: Automatically deploys a “Provisioner” VM (acting as a gateway, DHCP, and TFTP server) and one or more “Target” VMs.
- Simplicity: A clean CLI to create, start, stop, and clean up the entire lab.
- Cleanliness: All artifacts (disks, logs, configs) are stored in
~/.pvmlab/, keeping your workspace tidy.
Key Features
- Automated VM Creation: Quickly spin up Ubuntu or Fedora VMs from cloud images.
- Dual-stack Networking: The private network supports both IPv4 and IPv6.
- Direct SSH Access: Connect to any VM with
pvmlab vm shell <name>. - Cross-Architecture: Support for creating both
x86_64andaarch64VMs (host dependent for acceleration).
Architecture
pvmlab creates a self-contained environment on your Mac. Here is a high-level view of how the components interact:
The Provisioner VM acts as the router and server for the Target VM, providing DHCP, DNS, and the boot files needed for PXE booting. The socket_vmnet daemon manages the networking plumbing, ensuring isolation while allowing the Provisioner to reach the internet.
A Side Story: Diving into socket_vmnet
One of the key challenges I faced while building pvmlab was a conflict between macOS’s built-in DHCP server and my own. When you create a vmnet-host network using Apple’s vmnet.framework, macOS helpfully spins up its own DHCP server — which is great for most use cases, but a disaster when you’re trying to run your own dhcpd for PXE booting. The two servers would race to respond to DHCP requests, making the setup unreliable.
I discovered that QEMU’s native vmnet support already handles this via a net-uuid parameter, which internally sets the vmnet_network_identifier_key — an option available since macOS 11.0 that creates an isolated host network with no DHCP service. However, socket_vmnet (the tool that lets you use vmnet without sudo) didn’t expose this option.
So I dug into the vmnet.framework headers, filed an issue, and put together a pull request to add a --vmnet-network-identifier flag. The review process was a learning experience in itself — the maintainers pushed back on unnecessary complexity, asked great questions about the Apple API semantics, and helped shape the feature into something clean and focused. After a few rounds of feedback (and a bicycle incident that slowed things down), the PR was merged.
Interestingly, Apple is aware that this mechanism is confusing — a new vmnet_network_configuration_disable_dhcp() API is coming in macOS 26.0 (Tahoe) that will make this much more explicit. But for now, the network identifier approach is the way to go.
Use Cases
I built pvmlab primarily to have a sandbox for:
- Testing OS Installers: Iterating on Ubuntu Autoinstall or Preseed configurations.
- Network Boot Development: Developing and testing iPXE scripts.
- Simulated Bare-Metal: Simulating a real data center provisioning flow locally.
- Infrastructure Playground: Testing tools like Tinkerbell or MAAS.
- IaC Learning: Writing Terraform, Tofu, or Pulumi resources to declare and spin up/down VMs and provision them.
Getting Started
If you are on macOS, you can install it via Homebrew:
brew tap pallotron/pvmlab
brew install pvmlab
sudo pvmlab system setup-launchdCheck out the GitHub Repository for full documentation and usage examples.
Hope this could be helpful to people that want to learn about PXE booting and bare-metal provisioning!