Virtual Machines on the M1
For reasons that might become clear in a later post, I’ve been trying to get a simple Debian VM running on my M1, and it turns out this is not as easy as it was on Intel processors.
VM managers like VirtualBox / VMWare / Parallels are virtualization engines, not CPU emulators, which is to say that they cannot run/translate CPU instructions meant for x86 processers on ARM chips (and is a much harder problem). When Apple switched to ARM (like 2 years ago!), they made all these tools fundamentally incompatible with the new M1 machines. I’m also led to believe Rosetta2 doesn’t help in these cases because it runs at a higher level in the software stack. I might be wrong here.
If you’re looking for a simple GUI solution today, your best bet is UTM. It’s the closest you’ll get to the “just works” category. UTM is powered by QEMU, which is a CPU emulator (more on this later), so it can run both ARM and x86 OSes. They even have an iOS app, WTF! It does need jailbreaking as far as I can tell though, I’ve never needed a VM on-the-go to be honest.
VirtualBox also has an ARM build for the M1 these days, but they mark it as a “developer preview”, and boy is it Beta software like no other. I couldn’t get anything to work with it after trying for hours. Would not recommend, unless you’re reading this when it has stabilized.
The paid options (VMWare Fusion & Parallels) look more promising, especially because they’re not cheap, but I didn’t give these a shot, my experiment didn’t justify spending upwards of a €100 for software that 1: I didn’t know worked, and 2: I might not touch again.
If you’re looking for something more lightweight (like a Debian machine without a
desktop environment that you can SSH into), the story is a lot shakier.
is an open-source virtualization library for *nix systems that seems fundamental
for virtualization. I believe this does CPU emulation. Built on top of this is software
QEMU, which is what powers UTM. But we can pair
Vagrant to spin up VMs pretty easily (once you
figure out what’s going on, at least. I took me days to get to this point 😔).
First, we’ll install Vagrant & QEMU, which I see as a souped-up Docker. We’ll want to install
a QEMU plugin for it. I found vagrant-qemu,
which works splendidly. It has a few limitations (like lack of support for
but these don’t matter for what we’re trying to do right now:
$ brew install vagrant $ brew install qemu $ vagrant plugin install vagrant-qemu
Next, we’ll want to find a prebuilt Vagrant Box for Debian over at Vagrant Cloud.
Provider segmented control to
libvirt and search for
Okay spoiler alert: the official
bullseye image does not work. I gave it a fair
shot, but in the interest of getting somewhere, I ultimately decided to give up on
it and instead used this box,
which is based on Ubuntu instead (close enough).
Almost there — you’ll want to create a file called
Vagrantfile (no extension) somewhere
on your filesystem with these contents:
Vagrant.configure("2") do |config| config.vm.box = "perk/ubuntu-2204-arm64" config.vm.provider "qemu" do |vb| end end
and then run
vagrant up in this directory. Vagrant will download this box (which might
take a bit, be patient!), then use QEMU to boot up an Ubuntu VM! It’ll create a
directory, which will store this VM’s state. The CLI exits after
vagrant up by
the way, so if you want to check the status of your VM, you should be able to run
Now we can finally SSH into this machine by running:
$ vagrant ssh
Note that you’ll have to run this in the directory with your
Vagrantfile. Like I
said, Vagrant stores the machine’s state in this directory.
Once you’re done, you’ll probably want to shut the machine down using
(again in the same directory). If you want to delete this VM, you’ll do
(again in this directory).
One thought that entered my brain during all this was: what about Docker? I learned
that Docker images are built to be as lightweight as possible so they can be spun
up quickly, and as such, are incredibly barebones. If you boot up an Ubuntu image
on Docker, it doesn’t even have
sudo on it. More importantly though, Docker’s init
system is bespoke (or at least I think it is), which is to say PID 1 isn’t actually
systemd, which is extremely problematic for what I was trying to do.
You can install a full-blown OS on it though. I came across this article
that does exactly this, if you’re interested. Docker can also be used as the virtualization
engine for Vagrant, so this might be cool to you.
For now though, I like my Vagrant + QEMU setup a lot. This is cool tech.