Migrating NixOS from nix-channels to flakes
Nuuuuuu I didn’t open my nixos laptop for like 3 years and now everything is different and my nixos-rebuild switch is busted and apparently channels aren’t a thing anymore and everyone uses something called flakes??? ;_;;;;;;;;;
I gotta fix my computer
Whining aside, I need to get my system up and running again.
Resurrection plan
Here’s the rough plan:
- Update my current nix-channel to something more recent (I’ll choose latest unstable) and iterate running
nixos-rebuild switch
and fixing config errors until the command succeeds again - Identify what changes I need to make to have my system be “managed by flakes”, then make them
- My config files mean nothing to me!!!! Figure out what was going on with my last set up. Specifically, try and identify what I’m managing at the system-level via
configuration.nix
and what I’m managing at the user-level with home-manager. - Strip away any configuration (programs, ricing options, etc) that I don’t understand or can’t trace back a purpose for
- Once configs are cleaned up, add things back in as I need them
The rest of this will mainly be about step 2.
Wait, wtf is flakes
Nix folks are migrating to using an experimental feature called “flakes”1 instead of the officially supported nix-channels to manage where things are downloaded from.
Channels were the mechanism by which you told your NixOS system which version of nixpkgs to use when running updates, and this was managed outside of your nix configurations and resolved at the time of applying your configs. This means builds technically were not hermetic2 and therefore reproducible3 using the old system because the underlying state of inputs couldn’t be guaranteed. Flakes meanwhile introduce version locking for inputs (where nixpkgs is an input) via a flake.lock
file, similar to how cargo for rust/pip in python work. This makes builds actually reproducible because the exact version of your inputs are explicitly stated in your nix config files, which allows deterministically resolving the build results.
Note: flakes are still an experimental feature and nix-channels are what is officially support by stable nix (which made navigating the documentation super confusing!). There’s been some drama in the nix community around its adoption into mainline nix, but it seems like folks have largely gone the way of flakes afaict. Therefore I’m choosing to flakes in hopes of future-proofing my set up.
Another note: I’m discussing flakes vs channels here in the context of managing NixOS. I’ve yet to decipher how flakes play with (against?) nix for development, eg with a default.nix or shell.nix. Is it a full replacement? Can/should they be used together? tbd!
Actually migrating to flakes
Enable flakes
Since flakes are still an experimental feature, it needs to be switched on in your nixos configs. Add the line nix.settings.experimental-features = [ "nix-command" "flakes" ]
anywhere to your configuration.nix
and then rebuild your system with sudo nixos-rebuild switch
.
Initial flake.nix
I grabbed a very minimal flake.nix
from the thiscute.world guide as a starting point:
|
|
If you don’t know Nix language (I’m still learning!), the main takeaways about this flake.nix
is that there is an input block and an output block.
Inputs represent what you want to use for your package sources and must follow the input schema4 so that they can be processed into the lockfile properly. The only input we care about right now is nixpkgs
. Since nixpkgs is a git source, flakes will auto resolve the latest SHA in flake.lock
since we don’t specify one.
In outputs, though flakes support many different output types, the only one we care about atm is the nixosConfiguration
output type. You just need to specify your machines hostname on L11 in place of lolbox
and make sure your system on L12 is correct.
Actually lets put everything in a git repo
At this point I realize that I want to be doing all of this in my .nixfiles git repo (which can be wherever) rather than /etc/nixos
, so let’s move things over. My resulting directory looks something like this:
.nixfiles/
├── configuration.nix
├── flake.lock
├── flake.nix
├── common
│ └── # smaller modules...
└── hardware
├── hardware-configuration.nix
└── # smaller hardware-specific modules...
Now I can rebuild my flakes-backed system like this:
sudo nix-rebuild switch --flake ~/.nixfiles#lolbox
Note that you’ll need to git add .
your config files before rebuilds will work.
Side quests
Slightly reworking things to be “multihost-forward”
Though I don’t have multiple machines to manage just yet, I’m planning to add some nodes to my home network soon so it’d be nice to organize these configs towards that goal. My multihosted .nixfiles
directory structure will look something like this:
.nixfiles/
├── flake.lock
├── flake.nix
└── hosts
├── lolbox
│ ├── default.nix # lolbox's "configuration.nix"
│ ├── hardware-configuration.nix
│ └── # other hardware-specific modules...
└── someOtherHost
├── default.nix # this host's "configuration.nix"
├── hardware-configuration.nix
└── # other hardware-specific modules...
Now in flake.nix
I can add multiple hosts like this:
|
|
Then when rebuilding NixOS I’d specify the hostname target that I want to specifically run.
Adding home-manager as an input
I know I’ll need to set up home-manager to work with flakes as I continue reorganizing my configs, so let’s do that now. Note that there are multiple ways to do this, but I’ve decided to go the way of declaring home-manager input at system level (i.e., in my top-level flake.nix
). This way my home config changes get picked up as part of running sudo nixos-rebuild switch
and appear in the list of system generations rather than a separate home-manager generation list. This is nice because I don’t have to keep track of two lists, and I’ll keep it this way until the need arises to split them out.
I add a dir called home
in .nixfiles
to house any configs that should be managed by home-manager. I can throw configs for things like vim, i3 and other user-level programs in here. My .nixfiles
dir now looks like this:
.nixfiles/
├── flake.lock
├── flake.nix
├── home # dir for home configs
│ ├── default.nix # entrypoint to home-manager configs
│ ├── i3
│ │ ├── config
│ │ └── default.nix
│ ├── terminal
│ │ ├── default.nix
│ │ ├── tmux.nix
│ │ └── urxvt.nix
│ └── vim.nix
└── hosts
└── lolbox
└── # hardware-specific modules...
Here’s the updated flake.nix
:
|
|
In the inputs block, home-manager gets added as an input source. Then in the outputs block, we pass in the home-manager
input as an argument to the output function in L11. On L20 we add home-manager to the modules list, and we specify the module dir on L24.
We got flakes
My NixOS is now managed by flakes! wooo. Theoretically it should now be safe to completely clean up any traces/usage of channels in my system. Onto step 3, 4, and a neverending step 5 :3
-
My fave noob’s guide to flakes: https://nixos-and-flakes.thiscute.world/ ↩︎
-
a build is “hermetic” if its unaffected by the environment (ie libraries, compiler versions, etc) it is running in; typically achieved by having the build system refer to “uniquely identifiable” inputs, e.g. a specific version of package identified by a commit SHA ↩︎
-
a build is “reproducible” if building the same source + inputs always produces the same results. https://reproducible-builds.org/ ↩︎
-
https://nix.dev/manual/nix/2.18/command-ref/new-cli/nix3-flake.html#flake-inputs ↩︎