Here are some brief notes on running a virtual Linux box on an Apple Silicon macOS machine. There are many ways to do this, but I wanted something very simple, with a minimum of features.
Happily Apple provides a virtualization framework1 which does much of the hard work. To use this API from the command-line I use vfkit2: a third-party wrapper written in Go.
Disk images
Although it’s obvious in retrospect, I was surprised to find that vfkit can use the standard cloud images provided by e.g. Debian3.
For example you might download debian-13-genericcloud-arm64-20260819-2575.qcow2:
debian-13is the release of Debian;genericcloudimplies a kernel with fewer devices, but it works for me;arm64is the architecture;qcow2is a disk-image format.
Inside the qcow24 file you find the traditional Linux disk image. In principle you can modify this to suit your application but I found the tools to do that from macOS didn’t really work.
One notable exception: qemu-img is available in homebrew. This is
helpful because vfkit can’t handle qcow2 files, so you need to convert
them into a raw image. You can then resize the image as desired
(truncate lets you extend the file too).
QC=../debian-13-genericcloud-arm64-20260819-2575.qcow2
qemu-img convert -f qcow2 -O raw $QC debian.raw
truncate -s 10G debian.rawCloud-init
Given the difficulty of editing the VM you’re going to boot, we need a way to personalize it. This is exactly the problem that AWS has, and so, unsurprisingly, there’s a cloudy solution to the problem: cloud-init5.
The key idea is to supply a user-data YAML file when the machine starts
which can configure all manner of things. Here’s an example:
#cloud-config
users:
- name: mjo
groups: [sudo]
shell: /bin/bash
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
ssh_authorized_keys:
- ssh-ed25519 AAAAxxxx... mjo@foo.com
ssh_pwauth: false
package_update: true
packages:
- qemu-guest-agent
- curl
- sudo
- avahi-daemon
runcmd:
- echo "cloud-init ran" > /root/cloud-init-test.txtThis sets up a user and installs some packages. Hopefully this will be enough to boot the system and allow you to login via SSH.
It’s important that the file starts with #cloud-config.
N.B. This configuration only happens the first time a VM is booted, so if you make a mistake and leave the system unbootable, I found it best to start again from scratch.
We also need a meta-data file:
instance-id: toy-vf-01
local-hostname: toy-vf
Booting the VM
These notes assume that you’ll just boot a single VM: if you run more
than one, then you’ll need to pay attention to the network MAC address
and instance-id.
You just need to run vfkit:
R=/Users/mjo/toy
vfkit \
--cpus 4 \
--memory 4096 \
--bootloader efi,variable-store=$R/efi-vars.fd,create \
--device virtio-blk,path=$R/debian.raw \
--cloud-init $R/user-data,$R/meta-data \
--device virtio-net,nat,mac=52:54:00:70:2b:71 \
--device virtio-rng \
--device virtio-serial,stdio\
--restful-uri tcp://localhost:8081This does what you think it does. For details read the vfkit documentation6.
I found networking a bit unreliable when relying on a random MAC address.
Rather than trying to remember it, I save that command in a bash script
called start.
Controlling the VM
As you might expect from the command above, vfkit listens on a socket for commands.
I found two particularly useful:
Stopping the VM
http POST localhost:8081/vm/state state=StopGetting the VM’s status
http GET localhost:8081/vm/inspect
http GET localhost:8081/vm/stateThose snippets get saved as stop and status.
Using the VM
There is very little to say here: once booted you’ve got a normal Linux box to which you can login. There are no shared resources, though you could change the vfkit configuration to add them.
I found it very useful to install the avahi-daemon package which
advertises the machine at $hostname.local. So rather than comb through
files to find the VM’s IP address you can just ssh toy-vf.local.
References
- 1. https://developer.apple.com/documentation/virtualization
- 2. https://github.com/crc-org/vfkit
- 3. https://cloud.debian.org/images/cloud/
- 4. https://en.wikipedia.org/wiki/Qcow
- 5. https://cloud-init.io
- 6. https://github.com/crc-org/vfkit/blob/main/doc/usage.md
![Atom Feed [ Atom Feed ]](../../atom.png)