Santiago Calcagno

M1 dev setup using a NixOS virtual machine

2024 UPDATE: I ended up ditching this in favor of one of the Docker Desktop clients and dev containers. Sometimes I still think of integrating dev containers and nix using one the tools like and Devbox, but I haven’t taken a look at it yet.

For the last two months I have been doing dev work on an M1 MacBook Air. Kind of a big change in retrospective, having used Linux in bare metal almost uninterruptedly for 10 years, give or take. Needless to say at this point, the hardware is great and the main reason that made me go with it, and I did not find any blockers on the software side for the things I normally do.

One of the reasons I suspect made everything a bit smoother is being prepared to leverage my comfort using Linux to set up a virtual machine with UTM (a frontend for QEMU in macOS) or Docker containers using the Docker Desktop app for my work. Given that Docker in macOS uses virtualization to run a Linux kernel anyway, I decided to go with the “bare” VM approach for more tinkering control.

NixOS and Nix were also tools I wanted to start using more instead of on-and-off experiments as I was doing. I feel like giving even an intro to these projects would make this post quite long because they can do a lot! But I can mention that I felt attracted to try them, among others, because I can:

When I started looking into running NixOS using UTM, I could not find a proper ISO to boot with, so I went with Ubuntu + Nix to hit the ground running. This week I decided to search again and I finally could find an image in the UEFI section of NixOS on ARM. I do not know why I did not find it earlier, but eh. Incidentally I also found another reference in a blog post by a NixOS dev this week.

So the release and unstable ISOs can be found at Hydra, NixOS’ CI system. Search for “nixos.iso_minimal” there and you will get the latest built ones. I went with the nixos.iso_minimal_new_kernel.aarch64-linux from the unstable branch, mainly because I can roll back to a previous machine state easily from the boot menu if something breaks, but I imagine going with the release version would be OK as well. You can always pick single packages from unstable if you want to.

After downloading and mounting the ISO in a newly created VM, I followed the installation section of the manual pretty much verbatim. I did not have to configure GRUB as stated in the ARM section of the wiki for this setup. Also, take into account that you have to forward port 22 (or the one you use for SSH) to some other local port in your machine in the configuration of the VM in UTM to access it from macOS.

Visual Studio Code window showing NixOS as the output from neofetch in the integrated terminal

A big downside of committing to “the Nix way” at this point in time is that fixing broken stuff might need learning the language and digging through docs and issues which are not always clear or complete. So far though, working with Node.js, Docker, Docker Compose and VSCode through SSH has been flawless. The VM is really snappy and, as a small curiosity, it does not show up in the list of apps using “significant battery” where Docker Desktop would be at all times! For the occasional container that only has a x86_64 version I can always set up another VM with NixOS amd64 to replace the one I already have with Alpine, but I do not see the urgent need for it.

Keep in mind running Nix in macOS is also an option, one that is being nicely shaped thanks to the Nix 🖤 macOS Open Collective. As far as I read, it can work as a replacement for Homebrew. Some nice improvements might still need to wait for a backport or a new Nix release though. For me, needing Docker already made me go with the VM approach instead, and after some time I find the logical separation of integrated OS apps and the dev environment “cozy”, if that makes any sense.

A couple of elements I recommend complementing this setup with:

Finally, here’s my current configuration.nix file in case it helps with anything. Not much added from the base one though.