Image of memory DIMMs and PCBs in a purple/pink hue

My personal journey into NixOS

A look into my experience migrating from Ubuntu to NixOS and why I made the switch

TLDR: NixOS is great for development, works nicely for gaming (using flatpak), and rolling back to previous configurations feels like a superpower. But it is very much an operating system for people with prior Linux/Unix experience, NixOS is closer to Arch in setup than Ubuntu or Mint.

Hi, before we get to the main dish of NixOS let me give you a small entrée in the form of an explanation why I am leaving my current distro Ubuntu behind.

Lately I’ve been unconvinced by both Ubuntu and the choices of Canonical, this is not a novel concept. Besides the classic snap complaints, tweaks made to Gnome that did not feel sensible. So disagreeing with snaps and how they are forced on users, and unconvinced by the Ubuntu flavor of Gnome. Now dear reader, those sound like easy fixes, just move to Mint I hear you say. Well I had the same thought process, but then I investigated my experience using Ubuntu over the years more closely.

And because this article isn’t called ‘journey into Mint’ you might have guessed more things bother me, so what did I find? The general dev experience, which used to feel like a breath of fresh air, no longer felt adequate, and was not measuring up in some cases:

  • Getting caught up in dependency hell for Python, which I fixed with pyenv and the venv utility.
  • Running different versions of Dart, luckily the built-in tooling is better compared to Python, you can track different Git branches of Dart and point your compiler to those.
  • Troubles with migrating .NET Core 3.2 to .NET 6, Ubuntu 22.04 deprecated an older OpenSSL library necessary for .NET Core 3.2. This forced me to either get an older Ubuntu for that or rely on my dual boot Windows version.
  • In general a lot of packages used their own APT registries, which would make getting back up and running on a new machine more time-consuming.

The most bothersome about all of these points is that replicating the development environment was mostly up to my memories of how I did it last time. As you can imagine that’s an unreliable way of getting things up and running, especially if I broke my machine and had to install everything again in a hurry.

So with all this in mind, and having heard of Nix, NixOS started to pique my interest. It wouldn’t be more than an interest for over half a year, I felt too busy to migrate my OS. Come October 2023, I ordered parts for a new PC. With that it was time to for me to decide on which OS to install. Now was the chance to jump into NixOS and figure things out while still able to do time-critical work on my main Ubuntu machine.

Now this out the way it’s time for me to fulfill the promise I made in the title of this article and give you insight into my experience installing NixOS.

Installation of NixOS

NixOS has a graphical installer for various DEs, or you can use an existing NixOS config, but the graphical installer seemed a nice place to start. Wanting a change from Gnome I decide to pick the KDE installer and on I went. A pleasant surprise when booting from my installation media was that it contained various versions of the startup command, a small quality of life addition I could appreciate. This not being my first rodeo, and my new shiny setup containing an Nvidia GPU I picked the nomodeset startup version just to be sure.

Installation was rather easy and comparable to experiences you’ll find with other graphical installers, only ‘laborious’ change was setting up a custom boot size partition. (Increasing the size of your boot partition allows for storing more historic NixOS configurations) The standard UI does not allow you to resize them manually, it will only dynamically calculate partition sizes based on what options you select. But that is a minor gripe in an otherwise uneventful experience, which is how I like my installer experiences to be. Now with 8 GiB’s for my boot sector, which might have been overkill, and the remainder split up to root and swap. Where my hope is that I can someday get hibernate to work properly for me on Linux, I am on my way to the first proper boot.

First time booting

So some troubles with my first boot, I was presented with a basic bash terminal prompting me for my username and password, not the flashy KDE experience I expected. Well time for me to dive into the main feature of NixOS, the configuration nix files. Opening the config at /etc/nixos/configuration.nix I expected KDE to be disabled or missing, this was not the case. Onto major suspect number two, Nvidia drivers, at this point I had the foresight to actually look at the documentation for installing the Nvidia drivers. And wouldn’t you know it described perfectly how I should have added them in my configuration, having done that and after a reboot I had my fresh KDE look. (Not after getting tired of using nano and installing vim) So with a functional DE and an editor I could work with while I was figuring out a way to import my neovim config, I was ready to install the packages I want.

Installing packages

Time for packages, I want to start working on my new machine, so I start with setting up my dev environment, IDE, SDKs, Nextcloud etc. The standard way NixOS creates your configuration for packages is straightforward, two lists of packages, one for the system and one for the user, that’s it. This works fine in theory, but I would like to have some better readability as well as structure of my configuration. I want to approach it in the same sense I would approach an IaC project. On top of that there still is the case of my dotfiles, I also want to be able to manage these using NixOS or at the least have them in the same git repo.

Oh, yeah I think I did not mention that yet, my config is completely stored in git and publicly accessible, so if you feel like you need inspiration have a look there. Do note I am most surely not an expert, and feel free to copy (parts) of my config, but remember you will also copy all the mistakes I made.

But back on topic, although I didn’t know how I was going to handle my structure yet. However I certainly did know how I was going to manage my dotfiles: home-manager, it allows for managing settings of a lot of applications using Nix. And… reading this last sentence I think it’s time for a small intermezzo on what I mean with Nix, one that I’ve seen in almost every presentation on the topic.

Nix, Nix, and Nix

So for those that don’t know, the term Nix is quite an overloaded term, this is a very helpful graphic showcasing what I mean:

Nix triangle

Image taken from Shaun Loo

As I mentioned in the previous section, this image is a classic when it comes presentations on Nix, it shows us that there are three different meanings to the terms.

  1. NixOS is already introduced and most of the time is explicitly named this way, but it might be shortened to Nix, and is the operating system that is fully defined by nix and uses nixpkgs as package manager
  2. Nixpkgs, NixOS uses nixpkgs as package manager, but other distros, including macOS, can also use nixpkgs.
  3. Nix is also a functional programming language that is used by both NixOS and nixpkgs to create your configuration files, but as it is a programming language it can be used for other things as well, like solving advent of code.

Using home-manager and flakes

Now with that short intermezzo out of the way, let us focus back on installing packages, I want to use home-manager. Yet wanting to use it is different from knowing how to use it. I still have zero clue how I will structure the repository at this point.

So in my pursuit for a structure I also stumbled on flakes, which are only a vague concept for me at this time. But reading some pros and cons quickly convinced me to use these. But what are those great pros you say? Well simple really, in essence on top of using a config for your setup we now also add a lockfile for the dependencies that are used in your configuration. This allows anyone to take the config and get an even closer reproducible build when compared to just using plain Nix. Because in the end you and me might both use a different hash of the same version. Especially when pinning to unstable configuration outcomes might differ greatly over time.

One major problem with this would be the fact that maybe different machines need different firmware versions, luckily flakes can be setup to allow for different configuration files that can import from shared configurations.

With all this I was already starting to form ideas about the structure and how I was going to do things. Mostly dividing up my applications in a separate directory managed by home-manager, even when there is no native support for the application. This makes for a nice separation of concern, where all my personal packages I use I can find there.

And for the system packages a different directory with configurations for all my different machines. Now my ideas are still fuzzy at this point, so on I go looking for resources on how to create this configuration that is starting to accrue magical proportions in my head.

Awesome community resources

A major flaw I’ve heard repeated is the lack of documentation in some areas, but nonetheless there are some great resources on getting started if you know where to look. And what helped for me was looking at other peoples configurations.

The one I found most helpful was the starter config written by Misterio77, which is helpful for if you are looking at how to create a basic configuration, but also get a feeling of what you can actually do. And knowing what you actually can do has been the most difficult for me to discover with the current documentation. However I found that looking at Misterio77’s personal configuration repo gave me a great look into what is possible, this contains a great starting point for managing multiple machines whilst still sharing parts of the configuration between machines. Besides giving me insight for using multiple machines he does much more like using secrets, connect to tailscale, manage backgrounds, etc.

With all this I had the feeling I could create a competent config for myself, it most certainly won’t be the last I am sure, but it seems good enough for me. Speaking about my own configuration if you feel inspired to use NixOS and want more inspiration, you can look here on my GitHub.

Because I didn’t want this post to be an installation guide, and more of a rendition of my experience. But the definite configuration for my machines looks a bit like this:

│
├── flake.lock
├── flake.nix
├── home-manager
│   ├── apps
│   │   ├── default.nix
│   │   ├── app1.nix
│   │   ├── app2.nix
│   │   ├── app3
│   │   │   ├── dotfiles
│   │   │   └── app3.nix
│   ├── flatpak
│   │   ├── install_all_flatpaks.sh
│   │   ├── install_flatpak_games.sh
│   │   └── install_steam.sh
│   └── home.nix
├── nixos
│   ├── laptop 
│   │   ├── configuration.nix
│   │   └── hardware-configuration.nix
│   ├── desktop
│   │   ├── configuration.nix
│   │   └── hardware-configuration.nix
│   └── shared
│       └── default.nix
├── README.md
└── scripts
    └── setup-ssh-key.sh

As you can see there is a main distinction between system apps and user apps, in the NixOS and home-manager directories respectively. To create a clear distinction between user and system apps I also add ones that are not necessarily managed by home-manager. I also have room for storing dotfiles for applications that as mentioned can’t be managed by home-manager, or I want to still be usable without a nix configuration. One such application is neovim, I can then import my dotfiles from this repo to the local config.

And besides that I have different directories for different machines, so in this case my laptop and my desktop, and a shared section with settings they both use.

Now this is only the start of my NixOS journey, and if you want to follow along I’m planning my future posts to be more hands-on and showcase what I do.

  • I still want to show how I use home-manager with my applications.
  • Using nix-direnv to automatically load nix configuration in development.

Further reading

For the next blogpost on home-manager tbd.

For the next blogpost on using nix-direnv tbd.

Comments