For work I have an Intel Core i5 MacBook. With the Docker Desktop license changes I looked into minikube and Multipass but both failed when I got on our VPN with Cisco AnyConnect due to bridged networking in hyperkit (see links at bottom).
I realized I would be happy SSHing into a local Linux VM but passed on VirtualBox to avoid depending on legacy system extensions. I landed on libvirt for the VM, then realized I could remove libvirt from the equation to finally end up at this quick, simple setup for an Ubuntu VM on macOS with QEMU.
First, install QEMU (see https://wiki.qemu.org/Hosts/Mac for other options):
brew install qemu
Now create the file that will serve as the hard drive for the VM:
qemu-img create -f qcow2 ubuntu.qcow2 50g
Download an Ubuntu ISO file (or any other linux):
curl -LO https://releases.ubuntu.com/20.04/ubuntu-20.04.3-live-server-amd64.iso
Next, run this command to boot the VM (I put this in a script I call boot.sh
):
qemu-system-x86_64 \
-cdrom "ubuntu-20.04.3-live-server-amd64.iso" \
-hda "ubuntu.qcow2" \
-machine "type=q35,accel=hvf" \
-m "4G" \
-netdev "user,id=n1,hostfwd=tcp::2222-:22" \
-device "virtio-net-pci,netdev=n1,bus=pcie.0,addr=0x19"
Explanation of options (many of these are lifted from https://www.arthurkoziel.com/qemu-ubuntu-20-04/):
-cdrom
boots the VM from the ISO file for installation. Drop it on subsequent boots once Ubuntu is installed.-hda
specifies the hard drive file we'll be using.-machine
uses the latest machine type and the macOS native hypervisor-m
is the max amount of memory the machine may use-netdev
and-device
set up networking so we can ssh into the VM on port 2222
This command will open a QEMU window you can Alt-Tab to in order to monitor machine startup and proceed with installation. That window also has a handy "Power Down" dropdown option in the "Machine" item in the menu bar.
Once installation is done I add the contents of my public SSH key to ~/.ssh/authorized_keys
in the VM, then ssh in with the following (starting or attaching to a screen session on login):
ssh -i ~/.ssh/gsf_id_ed25519 -p 2222 -t localhost 'screen -D -R'
I also need to share directories between host and guest, but Plan 9 and virtfs are not available in QEMU on macOS(see update below!), so I use sshfs (IP address of host is 10.0.2.2):
sshfs 10.0.2.2:/Users/gsf/Downloads ~/Downloads
That command will set that ~/Downloads
mount until the next boot. To make it automatic, I generate an SSH keypair on the guest, add the public key to authorized_keys on the host, and make the sshfs mount on boot by adding the following to /etc/fstab
:
sshfs#[email protected]:/Users/gsf/Downloads /home/gsf/Downloads fuse _netdev,allow_other,uid=1000,gid=1000,idmap=user,identityfile=/home/gsf/.ssh/id_ed25519 0 0
That's it! I kept notes on discoveries along the way below.
Multipass and minikube use hyperkit, which sets up bridged networking. This is what clashes with the AnyConnect client:
- canonical/multipass#961 (comment)
- https://multipass.run/docs/troubleshooting-networking-on-macos
- kubernetes/minikube#6296
- https://minikube.sigs.k8s.io/docs/handbook/vpn_and_proxy/
I'm still on an Intel MacBook but looking for a solution that I can continue using on an M1 processor. That rules out xhyve: machyve/xhyve#218
This was also helpful in laying out steps for running Ubuntu with QEMU on macOS: https://graspingtech.com/ubuntu-desktop-18.04-virtual-machine-macos-qemu/
@gsf Great work! Thanks for sharing!!
Just a few FYIs if anyone is interested:
id
at a osx terminal.sudo port selfupdate && sudo port upgrade qemu
.