I’ve been meaning to write something for a while now, however before I can dive into a couple of more interesting topics here, I feel like it is the best to mention the transition to flakes. I’ll try and keep this one short, there are a bunch of other way more detailed posts on flakes already, however I didn’t find to many posts about how to switch from an existing configuration to using flakes and it seems to be quite doable.

On a freshly installed NixOS system, /etc/nixos looks like this:

[user@host1:/etc/nixos]$ tree -CL 1
.
├── configuration.nix
└── hardware-configuration.nix

If you’ve played around a bunch, have more than one machine and have at some point in time started using imports, it’ll probably look more like this:

[user@host1:/etc/nixos]$ tree -CL 1
.
├── configuration.nix -> hosts/host1/configuration.nix # symlinked nixos configuration (.gitignored)
├── hardware                                           # hardware specific config
├── hosts                                              # contains hosts configuration.nix files
│   ├── host0
│   ├── host1
...
│   └── hostN
├── modules                                            # modular configuration bits and pieces
│   ├── baseUtils
...
│   └── zfs
├── pkgs                                               # packages that aren't part of nixpkgs
├── services                                           # services that are hosted on the hosts
│   ├── host2
│   │   └── nextloud
│   └── host3
│       ├── gitea
│       ├── calendar
│       └── navidrome
├── users                                              # user configurations
└── vpn                                                # vpn configurations

In my case I’ve created a bunch of directories containing the bits and pieces of configuration I use. Among them is a hosts directory, which contains all the host-specific files in host-specific directories and I symlink the configuration.nix from there into /etx/nixos/, so I can conveniently execute nixos-rebuild switch. The whole /etc/nixos directory is a git repo I share among all of my machines and the .gitignore file currently contains a single line:

[user@host1:/etc/nixos]$ cat .gitignore
/configuration.nix

A typical /etc/nixos/configuration.nix then looks like this, note the paths are relative from the /etc/nixos/hosts/hostname/ directory:

{ config, pkgs, ... }:

{
  imports = [
    ../../modules/baseUtils
    ../../services/host2/nextcloud
    ../../users
    ./hardware-configuration.nix
    ./vpn/tinc/networkname
  ];

  boot.loader.grub.enable = true;
  boot.loader.grub.version = 2;

  boot.initrd.availableKernelModules = [ "e1000e" "virtio_pci" "e1000" ];

  ...

}

Before switching to flakes I decided to do a bit of housekeeping inside the /etc/nixos/hosts/hostname direcories. While this is strictly unnecessary, I kind of liked the idea:

I renamed /etc/nixos/hosts/host/configuration.nix to default.nix and created a new configuration.nix, which includes default.nix:

[user@host1:/etc/nixos/hosts/host0]$ tree -CL 1
.
├── configuration.nix
├── default.nix
└── hardware.nix

Now I can just split my system configurations up into the more host-specific part, which I very rarely have to look into, containing options such as boot.loader.grub and another much shorter part that contains, what the system is actually configured for in form of module imports. The new configuration looks so far pretty empty, since we’ve renamed the configuration to default.nix, we can just reference it using ./.:

{ config, pkgs, ... }:
{
  imports = [
      ./. # <- isn't this just beautiful
    ];
}

But back to topic: in order to start using flakes, we need be enable flakes on the system, so create ./modules/flakes/default.nix and import it in default.nix:

{ lib, pkgs, config, ... }:
{
  nix = {
    package = pkgs.nixFlakes;
    extraOptions = ''
      experimental-features = nix-command flakes
    '';
  };
}

Rebuild the system to enable the feature.

Then in /etc/nixos create a minimal flake.nix file, which contains a link to the hosts configuration.nix inside nixosConfigurations:

{
  description = "Oblivious Infrastructure";

  inputs = {
    flake-utils.url    = "github:numtide/flake-utils";
    nix.url            = "github:NixOS/nix/2.5.1";
    nixpkgs.url        = "github:NixOS/nixpkgs/nixos-21.11";
    nixos-hardware.url = "github:NixOS/nixos-hardware";
  };

  outputs = { self, nixpkgs, nix, ... }: {
    nixosConfigurations = {
      host0 = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          ./hosts/host0/configuration.nix
        ];
      };
      host1 = nixpkgs.lib.nixosSystem {
        system = "x86_64-linux";
        modules = [
          ./hosts/host1/configuration.nix
        ];
      };
    };
  };
}

In order to check if everything works just run nixos-rebuild switch --flake .#host from /etc/nixos/ or nixos-rebuild switch --flake /etc/nixos/#host from anywhere else.

Congratulations, your system now runs on flakes.