Running Linux microVMs on macOS (M1/M2)

on slp's blog

Sometimes, while working on macOS, you may find the need to test something quick on Linux, or use some utility that's only available on this OS. But, of course, you don't want to go through all the process of creating the VM from scratch.

The good news is, you don't need to! Using krunvm you can create and start a microVM from a regular container image (that is, an OCI image), in just two commands and a couple of seconds.

Installing krunvm on macOS

If you're already using Homebrew, getting krunvm installed is super easy:

brew tap slp/krun
brew install -s buildah libkrunfw libkrun krunvm

Creating a volume for krunvm

The first time you execute krunvm, you'll get this message:

On macOS, krunvm requires a dedicated, case-sensitive volume.
You can easily create such volume by executing something like
this on another terminal:

diskutil apfs addVolume disk3 "Case-sensitive APFS" krunvm

NOTE: APFS volume creation is a non-destructive action that
doesn't require a dedicated disk nor "sudo" privileges. The
new volume will share the disk space with the main container

Please enter the mountpoint for this volume [/Volumes/krunvm]:

krunvm uses virtio-fs to share files between macOS and the Linux microVMs and, since Linux requires a case-sensitive file system, we need to create one on our system to be used to host the expanded container images.

Luckily, this is as simple as running the command suggested in the message:

diskutil apfs addVolume disk3 "Case-sensitive APFS" krunvm

Creating and starting your first microVM

Lets suppose you want to run a Debian microVM. As there are official Debian container images published on Docker Hub, this is as simple as running:

krunvm create --name debian-microVM debian

In this example, debian is the name of the container image used as source for the microVM, and debian-microVM is the name given to the microVM itself. Instead of debian you can use the name of any container, the same way you would when running a container with docker or podman. In this other example, the container image is specified including the registry, the image name and the tag:

krunvm create --name fedora-rawhide

Once created, you can start the newly created microVM this way (it should take less than a second to start up!):

% krunvm start debian-microVM
# uname -a
Linux debian-microVM 5.15.52 #1 SMP Mon Jul 18 08:47:44 EDT 2022 aarch64 GNU/Linux

By default, krunvm will execute /bin/sh in the microVM, but you can run any other command present on it by specifying it in the command line:

% krunvm start debian-microVM /bin/bash
root@debian-microVM:~# echo $SHELL

In fact, you can also run non-interactive commands and pass additional arguments to them:

% krunvm start debian-microVM /bin/ls -- -al /root
total 12
-rw------- 1 root root  12 Jul 28 20:49 .bash_history
-rw-r--r-- 1 root root 571 Apr 10  2021 .bashrc
-rw-r--r-- 1 root root 161 Jul  9  2019 .profile

And, without any kind of configuration, the network is completely funtional, so you can start installing packages right away:

% krunvm start debian-microVM
# apt update
Get:1 bullseye InRelease [116 kB]
Get:2 bullseye-security InRelease [48.4 kB]
Get:3 bullseye-updates InRelease [44.1 kB]
Get:4 bullseye/main arm64 Packages [8069 kB]
Get:5 bullseye-security/main arm64 Packages [167 kB]
Get:6 bullseye-updates/main arm64 Packages [2600 B]
Fetched 8447 kB in 1s (5686 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.

Sharing files with the microVM

krunvm allows you to share volumes from macOS with the Linux microVM by using the -v/--volume argument, which can be specified with both the create and the changevm commands.

Let's change the Debian microVM we've created before to give it access to $HOME/Public (macOS) through /Public (Debian):

% krunvm changevm debian-microVM --volume $HOME/Public:/Public
% ls ~/Public
Drop Box
% krunvm start debian-microVM
# ls /Public
'Drop Box'

Exposing ports from the microVM to macOS

It's also possible to run a service in the microVM and expose it to the outside by using the -p/--port, also available with both the create and changevm.

Once again, let's update our Debian microVM, this time to expose port 80 on the microVM to the port 8080 on macOS:

% krunvm changevm debian-microVM --port 8080:80
% krunvm start debian
# apt install -y python3
(long output omitted)
# echo "Hello!" > index.html
# python3 -m http.server 80
Serving HTTP on port 80 ( ...
(from another terminal on macOS)
% curl

Final words

krunvm is just a simple CLI utility. The heavy lifting here is done by buildah, libkrun and the Hypervisor.framework

If you find it useful, or you have any suggestion, I'll be happy to hear from you on Twitter or Mastodon. If you have found a bug, please consider creating an issue on GitHub.