For some time, I’ve been very interested in NixOS and the nix package manager. Most of my own development work happens on macOS, but like many developers, the travails of psuedo reproducible builds has always hampered my workflows. Managing different runtimes of programming languages, different package versions across projects, and different configurations has created a bit of a hodgepodge local development environment. While some of these configs I can carry over between machines and environments (via dotfiles), my current system is far from truly reproducible. I don’t love that the environment I work in everyday is a culmination of carefully crafted chaos.
Nix on macOS seemed like a bit of a welcome reprieve – I found flakes to be useful for various projects I was working on, but still, it was difficult to really implement these ideas within my organizations at work given the investment in learning required to get up to speed with nix. I do, however, feel strongly that the nix paradigm is one that can really transform computing. Declarative, reproducible environments will fundamentally reshape the way we think about, produce, and consume software.
In this series of dev logs, I will capture my experience of working with NixOS and its various satellite technologies: the nix language, nix flakes, and other tools. The goal is to get familiar with the nix ecosystem, and evaluate its capability to completely replace my dev environments and workflows.
In order to begin my journey, I wanted to spin up my new machine and configure NixOS from scratch. I could have also just done this via a VM on one of my other machines, but I had wanted to run a true standalone linux box for some time, so I took this as an opportunity to do so.
I’m working on a Framework desktop, with the following specs: AMD Ryzen™ AI Max+ 395 - 64GB.
Ultimately, I hope to build a configuration that I can then consume via VMs on my work and personal Mac machines, much like Mitchell Hashimoto does. There are many great advantages to this sort of setup, and Mitchell does a great job of explaining them. Take this quote from the README of his system configs:
Inevitably I get asked why? I genuinely like the macOS application ecosystem, and I’m pretty “locked in” to their various products such as iMessage. I like the Apple hardware, and I particularly like that my hardware always Just Works with excellent performance, battery life, and service. However, I prefer the Linux environment for almost all my dev work. I find that modern computers are plenty fast enough for the best of both worlds.
This perfectly summarizes my own feelings. The Mac ecosystem is incredibly powerful, and it’s difficult to move all of my computing needs to a linux ecosystem. The power of modern machines makes it possible to have the best of both worlds via a poly-OS system.
I installed nix using the graphical installer. Through balenaEtcher, I was able to flash a USB drive with the graphical installer, which I could then boot from my machine. I chose GNOME as my desktop environment, since it felt like a stable, suitable, beginner choice. I might change this later on, but I’ve had no complaints as of yet.
When I first got booted up, I wanted to install some familiar software, and after a bit of googling around and exploring system config files, figured out the place to install that was in /etc/hosts/configuration.nix
, under environment.systemPackages
:
environment.systemPackages = with pkgs; [
vim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default.
wget
curl
firefox
google-chrome
neovim
git curl ripgrep fd
spotify
ghostty
bitwarden-desktop
gdu
tailscale
typora
];
These are just some initial programs I installed. I wanted to get a feel for the ergonomics of the NixOS ecosystem, and getting my system setup with familiar tools was a good way to do that.
While running the installer, I specified that I wanted to allow installing unfree software, and that config was reflected here as well:
# Allow unfree packages
nixpkgs.config.allowUnfree = true;
The configuration.nix
file also defines your user configurations:
users.users.leon = {
shell = pkgs.zsh;
isNormalUser = true;
description = "Aumit Leon";
extraGroups = [ "networkmanager" "wheel" ];
packages = with pkgs; [
];
};
I didn’t mess too much with this for now, but I did configure my user to use zsh
, my shell of choice. I want to tweak my prompt and my styling, but I will tackle that as I think about how to idiomatically manage “dotfiles” in the nix ecosystems.
I also had an extra SSD that I wanted mounted and made available to the OS. I went ahead and added configurations to have that device mounted on each system rebuild/boot:
fileSystems."/data" = {
device = "/dev/disk/by-uuid/841f10ff-60cd-4a66-9cf7-7fbbb3940a9b";
fsType = "ext4";
options = [ "nofail" ];
};
This is good progress so far. We have a system booted, some initial configurations, and some software installed! In future dev logs, I will catalogue my approach in improving my configuration, and building towards a setup that can become my canonical development environment.