How were configuration monorepos managed before Flakes?

As someone who started their Nix journey around the end of 2022, most of my experience has been with Flakes. I enjoy them a lot, but as I’ve learned more about them and Nix in general, it has made me curious to explore alternatives and how things were done before they became so prominent in the community

I’ve already found most common practices, like default.nix files containing a top-level package set (and maybe some other stuff from time to time), release.nix describing CI-ish jobs and evaluating things on a per-system basis – rather than the more imperative { pkgs ? import <nixpkgs> { } } found in a default.nix – and of course the still pretty common shell.nix. When it comes to configurations though, it appears to be much more unclear

From what I have gathered, tools like NixOps, Morph, etc. were some common choices for this kind of thing. They feel a bit like overkill for a handful of local machines though (such as a desktop and laptop), and AFAICT local deployment support is iffy outside of more modern solutions like Colmena

Another method I’ve seen somewhat often is simply setting nixos-config in NIX_PATH. I remember seeing this a bit before I switched to Flakes, and it’s how I’ve mostly assumed this would work, with development shell hooks emulating behavior similar to nixos-rebuild:

mkShellNoCC {
  shellHook = ''
    export NIX_PATH="nixos-config=${./configurations}/$(cat /etc/hostname):${NIX_PATH:-}"
  '';
}

Lastly, as of 24.11, nixos-rebuild supports the --file and --attr flags. I haven’t been able to find much usage of this yet, but it seems like a pretty nice alternative to both of the above, and a lot closer to the (IMO) better UX Flakes users have in this situation. Not really sure what the best way to organize configurations for these flags would be though – maybe a configurations.nix?

Anyways: how far off the mark am I with this bit of NixOS history? Were env var manipulation and deployment tools really the best approaches to managing multiple configurations pre-Flakes? Was it not as bad as I think? How did you handle these things years ago?

Thanks for any answers in advance :slight_smile:

1 Like

Are you asking this for historical interest or because you actually want to know what’s possible sans flakes in the present-day?

I just have had a Nixpkgs checkout or two lying around since before Git migration. So everything can just refer to them, and I can update them as needed and roll back if necessary.

(Not using flakes and not going to anytime soon)

Mainly historical interest
but I do also wonder how some would adopt their workflows to today’s tools

Ah, I think I had seen this before in older Nixpkgs revisions! I wondered if people still did it, since (obviously) it’s totally possible and not really far from what other distros like Gentoo do to sync their repositories

Those are tools for devops on clusters, not personal desktop configuration. I would not suggest using them just to change where your configuration is read from.

Yep, environment variables are the intended solution here. The UX doesn’t strike me as particularly worse - you can also set your nix path in your configuration (with nix.settings), so if you take the multiple-entrypoint approach - that I think is still the most idiomatic way to set up even a flake - you only have to evaluate the config with the env variable the first time you deploy the machine.

Really all flakes give you for a NixOS config is the inputs management, which can be replaced with niv (previously npins) these days, and people mostly just used channels for - and channels are pretty ok, if you’re keenly aware of the edge cases.

I didn’t quite go with niv: how I handled it was usually something similar to the examples in androidenv: a function with a nixpkgsSource argument (this could just default to <nixpkgs>), and a pkgs default based on importing that.

I also used nixops for multi-node deployments, which worked something like this for multiple machines, so I’d have a shell.nix that would do the above nixpkgs pinning and set NIX_PATH in the shellHook. It made pulling in unstable a matter of changing the hash in the deployment shell, then using it as I wanted with overlays.

These days I mostly just do that with flakes and deploy-rs, but when you have a nixpkgs import it’s mostly up to taste for how you manage the pins anyway.