This why flakes start to shine!

Initially I had this section at the end, but in this case putting the conclusion at the beginning here might help motivate someone to try this for themselves. The crux of the situation is that the flake.nix file defines the package versions and ‘channels’ so to speak. It’s like a level higher than the configuration.nix. You import your configuration.nix as a module into the flake. Now you can rebuild –flake and that means 1. you don’t have to switch channels first to get the same options as I have defined and 2. all the versions of stuff are exactly exactly the same, meaning once you switch into what you’ve built it will match the last time the repo was written without any doubt.

What makes this awesome is it’s completely backwards as to how I’ve grown up and gotten used to Linux. What you’re doing here is fiddling around, yes, but you’re defining a final outcome not messing around with different steps to find a desired outcome. Because everytime you change/fiddle with something you’re picking the end result, the fear of the ever evolving “I’m not quite sure how I got here, what did I install before what and when” sort of fear is entirely gone.

The final piece of this puzzle is now home-manager. Beforehand 5 or so years ago when I used this I sort of half split my setup between it and configuration.nix just sorta because. Now though, realizing I can use it to define paths, think this is the clincher. In home.nix I have defined that I want the config directory within my dotfiles to be the system ~./config directory. Now, literally because all the version control is packaged along with the configurations, and there are hard links defining the dotfiles to their correct spots, you can make an exact copy of my whole system.

     file.".config/waybar" = {
       source = ./config/waybar;
       recursive = true;
     };
     file.".config/networkmanager-dmenu" = {
       source = ./config/networkmanager-dmenu;
       recursive = true;
     };

I used to have issues even on NixOS because when we are out actively sailing I just use my computer for writing and watching movies essentially. There was a 2 year period where I didn’t update anything. When I did go to rebuild half the syntax had changed, options were deprecicated, and I had to do like half an hour of fiddling again to get it to rebuild. In theory this could still happen, but because the versions and channels are also definied here, it’ll happen all together at once and everything will stay coherent.

Why?

Honestly, they’ve been out there, lurking in the background for years now, but I’ve never really given them a go. Because fiddling around is interesting and I ususally learn at least something, it seems like a project worth attempting.

How?

Well, with the help of Evan of course. No way I’d figure this out on my own.

So here we are, after a long call there are now a flake-a-fied version of my dotfiles on Evan’s Github just waiting for me to give them a go.

Attempt 1

Great start, great start. I spun up a NixOS VM with the hopes of perfectly recreating my setup as is on my laptop to start from. This way I could go through the process, see where it goes off the rails, ask about it, and see if through before making the switcharoonie on my actual machine. Up boots the new VM, all defaults, I copy my NixOS configuration.nix and accompanying files over, anddd….. already I broke it…

Going to reinstall a new fresh VM and try again. It’d be suhweet to be able to boot a fresh NixOS install and do one “rebuild flake github.com/mydotfiles” or whatever and have it just work right off the rip. Maybe, just maybe, we’ll get there

Round 2

Alright redid the VM and aside from discovering that wayland stuff works horribly in a VM, I was pretty easily able to replicate more or less the setup I have actually installed on my machine. I chucked in Xmonad as well because, well, X11 seems to work a lot smoother.

Applying Evan’s Flake

So here we go, I cloned again Evan’s version of my dotfiles and CD’d my way in there. First then I realized I have to enable flakes on the actual system before anything could happen. So I added the infamous line to the /etc/nixos/configuration.nix and rebuild. I then copied the /etc/nixos/hardware-confirguration.nix to the dotfiles directory so I wasn’t trying to build my laptop’s specs into a VM. I think that’s actually where I go tripped up in the above section.

We found we were missing a }; which is just classic, and then whapow it seems to work? It rebuild with sudo nixos-rebuild switch --flake .#nixos with #nixos being the hostname and . because I was already in the directory that the flake.nix file was sitting in.

Home Manager

I’m off on my own at this point. I always feel bad continually bombarding Evan with questions about stuff half the time I find I solve just after I ask lol. This time around it was the same.

Now that I have this working the actual concept behind flakes is making a lot more sense. Essentially, sort of like how NixOS makes it pretty easy for your setup to be portable on an install, flakes takes it a bit further in the ability to tie it all together.

Beforehand to reproduce my setup which was defined in /etc/nixos/configuration.nix all in one file, you could copy this file to your machine, keep track of the hostname and stuff so you didn’t like make a new user and what not, and rebuild switch. Home Manager

Now that I have this working the actual concept behind flakes is making a lot more sense. Essentially, sort of like how NixOS makes it pretty easy for your setup to be portable on an install, flakes takes it a bit further in the ability to tie it all together. At this point you’d have all the same applications as I. You would have to switch channels and rebuild again though to get on the same versions, maybe even to enable a few of the options. Often times this meant you’d go through a few cycles of trying to rebuild, getting some error, commenting out a line, rebuilding to change channels, adding lines back in, and so on.

Unfortunately you’re not done yet. With the same programs now, you need my settings as well which are classic dotfiles in ~/.config/. To do this you take the config directory in my dotfiles and copy the contents over. Usually at this point, because some packages have different versions or something, you’ll be like 90% there. This is far far easier than starting from a fresh Arch install and trying to piece by piece copy my setup, but it isn’t a one and done exact copy.

The Technical Breakdown

So that it’s possible to look here and figure out the anatomy of the one folder full system config I’ll breakdown my flakes/NixOS setup. First and foremost is the all important flake.nix file at the head of the directory structure.

Flake.nix

So this is the “inputs and outputs” file that starts the special sauce. The inputs at the head point to where modules live out on the interwebs. Here is where we are essentially replacing the channels part of the standard NixOS setup. Super cool because one it means the options you’ve defined are always going to be available when you rebuild, and two, you can add other links, not just NixPkgs for home-made modeles which, as NixOS is becomming more popular, is more and more common to come across.

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs";
    # These are some flake helper libraries that make writing a flake nicer.
    flake-parts = {
      url = "github:hercules-ci/flake-parts";
      inputs.nixpkgs-lib.follows = "nixpkgs";
    };
    systems.url = "github:nix-systems/default";
    nixvim = {
      url = "github:nix-community/nixvim";
      inputs.flake-parts.follows = "flake-parts";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.systems.follows = "systems";
    };
   stylix = {
      url = "github:nix-community/stylix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
   home-manager = {
     url = "github:nix-community/home-manager";
     inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = inputs:
    inputs.flake-parts.lib.mkFlake { inherit inputs; } {
      systems = import inputs.systems;

      perSystem = { config, inputs', pkgs, system, ... }: {
        _module.args.pkgs =
          import inputs.nixpkgs {
            inherit system;
            overlays = [
              (_: _: { inherit inputs; })
              (import ./overlays/packages.nix)
              (import ./overlays/nixos-configurations.nix)
            ];
          };
        legacyPackages = pkgs;
      };
    };
}

Overlays

Here is where my understanding is a little lacking. The outputs section here is a bit more bispoke lets say than the standard flakes setup people seem to have. It’s possible to have just the flake.nix file, mention your configuration.nix file in there, and essentially go away from there. The reason we have this seperated is mainly due to Evan’s genius and professional experience with Nix. Unfortunately you may just have to ask him.

Important to notice here is this is where you out the outputs which will allow you to use the options or modules in whatever other things you add into the flakes.nix inputs section. When reading a guide and it has you add a link in inputs then the module in the outputs, this is actually where I’m putting the outputs. Notice the home-manager addition here.

{
  nixosConfigurations = {
    liam-tpad =
      final.inputs.nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        pkgs = final;
        modules = [ 
	../nixos/configuration.nix 
          final.inputs.home-manager.nixosModules.home-manager
	   {
          home-manager.useGlobalPkgs = true;
          home-manager.useUserPackages = true;
	  home-manager.backupFileExtension = "backup";
          home-manager.users.liam = import ../home.nix;
	   }
	];
        specialArgs = { inherit (final) inputs; };
      };
  };
}

Other thing to note is this is where my original configuration.nix is imported. At this point there are other files which are imported there and I think I could change those to here if I wanted. Maybe I’ll give that a whirl just to see.

Dotfiles Structure

Now income the dotfiles legacy distro style. These I have in their own directory and, as shown at the top, are sort of remapped to where they normally live, making this whole directory structure portable on it’s own.

Useage

There are a few strangities now that everything is managed like this. Changing dotfiles now I do that in ~/dots/config/whatever.conf and then have to do a doas nixos-rebuild switch --flake . or test if I’m going to be doing lots of rebuilds just to try stuff out.

Updates now are also a little bit different. The flake.lock file is automagically made, this is what is setting all the version controls. So when you run a rebuild it builds all the packages, as listed and configured everywhere, based off the links and versions as defined there. That means if I rebuild in 10 years without changing anything, nothing will change, how cool! But to do updates you gotta change up the versions obviously. doas flake update when run in this directory updates all those links to the newest versions, then the classic rebuild switch will rebuild everything thus with new versions.