Looking for setups/blog not using flakes

Hi!

I am very aware this touches on a heated topic, that has divided the community. This post is not meant to start a discussion, but a conversation.

I am just looking for setups (or blogs that discuss) that are not using flakes, which setup multiple systems using other projects (which might use flakes) to maintain/deploy multiple nixos configurations from a git repo.

I initially started out using flakes, as they seemed easier to get started with, but have always been wondering how to achieve the same goals using stable features.

I have not been able to find a lot of material on this topic though, but maybe I’ve been looking in the wrong places.

Are there people willing to share their repos, setups or tools they use? I am also interested in relevant docs or recent blogs.

Please feel free to comment!

4 Likes

Not a blog post, sorry, but the release notes for 26.05 show the new system.nix:

# system.nix
let
  # Pinned Nixpkgs archive
  #
  # Use `curl -I https://channels.nixos.org/nixos-26.05` to get the
  # latest commit of the stable channel and `nix-prefetch-url --unpack`
  # to compute its sha256 hash.
  nixpkgs = builtins.fetchTarball {
    url = "https://github.com/NixOS/nixpkgs/archive/c217913993d6.tar.gz";
    sha256 = "026mprs324330pfazlgbw987qmsa8ligglarvqbcxzig2kgw0lqg";
  };
in
import "${nixpkgs}/nixos" {
  # Build NixOS using an external configuration.nix file,
  # or directly set your options here
  configuration = ./configuration.nix;
}

If instead of defining a single import ${nixpkgs}/nixos { configuration....}, you define an attrset of { host1 = import.... ; host2 = import... }, then you would use nixos-rebuild -f system.nix -A host1 switch. You can omit the -f system.nix if it is placed in /etc/nixos/.

Pinning in the release notes example is using builtins, but there are other third-party tools like npins, nixtamal, niv, yea, pinch, etc.

IIUC, before system.nix, the more typical way would be to have separate host{1,2,3}-configuration.nix files, which would be passed with -f. See for example infra/build at main Ā· NixOS/infra Ā· GitHub which has individual host/default.nix configuration for build machines, which import from github:NixOS/infra/modules

7 Likes

That’s cool, I didn’t notice that in the notes. I have been running something akin to that for a while: default.nix Ā· master Ā· Benjamin Edwards / nixnix Ā· GitLab it works great.

I’d also add that copperhouse/default.nix Ā· master Ā· Benjamin Edwards / nixnix Ā· GitLab and the etc link below are lifted from Pinning NixOS with npins, this time without flakes for real | piegames.de and work exactly as described on the tin. Has worked flawlessly for me for years.

4 Likes

Your default.nix is really cool! What command do you use to switch to lamorna.home and macbook?

I should also probably mention that flake inputs might either be handled by let... in assignments or else as function arguments depending on context.

For example, a default.nix providing some override to a package might look like:

# default.nix
let
  nixpkgs = builtins.fetchTarball {
    url = "https://github.com/NixOS/nixpkgs/archive/c217913993d6.tar.gz";
    sha256 = "026mprs324330pfazlgbw987qmsa8ligglarvqbcxzig2kgw0lqg";
  };
in
{
  pkgs ? import nixpkgs {},
  ...
}:
pkgs.hello.overrideAttrs (oldAttrs: {
  name = "my-hello";
})

building:

You can then nix-build default.nix to use the pinned version, since nix-build of a function with default arguments will ā€œmagicallyā€ pass all default arguments.

If you want something like flake’s follows, that gets replaced by either manually passing an argument in a .nix file like import ./default.nix { pkgs = import... } or on the command line like nix-build --args pkgs "import...".

ephemeral shell:

Very annoyingly, I haven’t found a pattern for nix-shell that is as nice as

nix run flakref#output-attr

To use the above default.nix in a nix-shell, I do something like

nix-shell -I myhello=/abs/path/to/myhello/folder/ -p "import <myhello> {}"

The /abs/path... can be replaced with $(pwd) or a URL to a github tarball as well

I would love it if someone has some better pattern for calling this, or if there was some parity in syntax for nix-shell to call into third-party repos. To be fair, I suppose I could also write a shell helper.

1 Like
nh home build -f ~/src/mine/nixnix lamorna.home

The standard home manager standalone entrypoint doesn’t allow this style of configuration unfortunately.

1 Like

There’s also Organizing your Nix configuration without flakes, which is similar to @bme’s setup.

2 Likes

It’s also worth noting that the notes there on special args are stale now. Partly inspired by that blog, I authored this incredibly large PR to plug specialArgs into the default entry point.

EDIT: my timelines must be crossed on this, because the blog mentions my PR. I’m old, that’s my excuse and I am sticking to it.

3 Likes

Even though it now supports specialArgs, I think the nixos/default.nix entrypoint is still worse than the nixos/lib/eval-config.nix one. It doesn’t even support specifying a list of modules! eval-config.nix is what lib.nixosSystem uses, and works like you’d expect:

# system.nix
let
  sources = import ./other/npins;
  # Read impurely from `builtins.currentSystem`. feel free to specify
  # system if you'd like
  pkgs = import sources.nixpkgs { config.allowUnfree = true; };
  nixosSystem = import (sources.nixpkgs + "/nixos/lib/eval-config.nix");

in {
  some-hostname = nixosSystem {
    # optional, but if you need pkgs outside of this scope,
    # then reusing it here prevents it from imported twice.
    # good for evaluation perf!
    inherit pkgs;

    specialArgs = ; # your special args here
    modules = ; # your module list here
  };
;
1 Like

why is it worse? If you pass the pkgs already configured then your module options for configuring the pkgs attrset don’t work. That seems like a strict downgrade to me?

edit: what do you mean doesn’t support passing modules?

https://gitlab.com/bme/nixnix/-/blob/master/default.nix?ref_type=heads#L21

what do you think this is doing? :slight_smile:

Two points.

why is it worse?

My primary issue with the function is that it takes a single module rather than a list of modules. You can achieve the same thing with imports, but coming from lib.nixosSystem, I just wanted to recreate the same behavior.

If you pass the pkgs already configured then your module options for configuring the pkgs attrset don’t work. That seems like a strict downgrade to me?

Sure, you can’t use those options (although you can set the same things outside of those options, as you see with my config.allowUnfree setup). But, if you want to do something like:

let
  # ...
  wrappers = import ./wrappers.nix { inherit pkgs; };
in {
  some-host = nixosSystem {
    inherit pkgs;
    specialArgs = { inherit wrappers; };
    modules = []; # your modules here
  };
}

Since we want access to pkgs in our set of wrappers, and it’s passed in specialArgs, we need to define it outside of the scope of the nixos system. If we didn’t do inherit pkgs, then the module system would reimport it, which is approximately a 0.25s delay.

Ok, I think the complaint about multiple imports is… not worth talking about consider configuration.imports = [ ]. Agree to disagree. On the not caring about options: I think for a single machine configuration that makes sense. If you want to configure multiple machines, then you can’t assume they all look the same. You also are assuming that you need to import pkgs outside of the configuration. You don’t. The whole point of the specialArgs patch is to be able to feed something like sources into modules, which can receive the configured pkgs, exactly once.

I said ā€œoptional, but if you need pkgs outside of this scopeā€!

My bad. Then I guess I don’t really understand why it’s worse.

  1. You don’t have to pay a double import perf penalty if you don’t want to.
  2. You can import multiple modules
  3. Bonus: you can use pkg configuration options.

I think it’s fine to prefer calling eval-config.nix, I just don’t see any objective criteria making it better.

That’s why I said ā€œI thinkā€!

1 Like

One correction I have to issue for myself: I said you can’t configure the passed in pkgs, and that’s not strictly true! you can append overlays which is useful to know.

I second the system.nix recommendation, as it’s meant to be a (limited) equivalent to flake.nix, and btw:

I doubt it, the heat comes when people ask questions like ā€œwhen will flakes be stableā€ or heavily criticise/praise flakes, because that becomes opinion based and everyone has their 2c (not excluding myself). I don’t think anyone will personally take offence that you want to not-use flakes.

5 Likes

Humbly submitting my own setup, using npins: GitHub - wbadart/dotfiles: Just my dotfiles and a couple handy shell scripts Ā· GitHub (feedback more than welcome! :smiley:)

I know you asked for setups managing multiple machines; you’ll have to scroll back in time to before I retired the second machine I was managing under this setup. Switching is a little awkward, in part since nix-darwin leans channel-heavy.

I use this as-is on my personal machine, and as an input to my work dotfiles, which:

  • are based on flakes,
  • mix in some company-specific config, secrets, and private packages,
  • and uniformly configure the MacOS host (just home-manager, no nix-darwin) and a NixOS VM.

If you want something like flake’s follows, that gets replaced by either manually passing an argument in a .nix file like import ./default.nix { pkgs = import... } or on the command line like nix-build --args pkgs "import...".

This 100%.

I’ll also link A nix shell with a nix package in a git repo - #5 by tejing

which has a pattern for a package.nix/default.nix that can be either imported or callPackageded easily. The basic idea is to write in a callPackage pattern, but define the arguments/package dependencies in terms of a pkgs argument that can take a default value.

i.e.

{
  pkgs ? import <nixpkgs> {},
  # pkgs ? import (import ./npins).nixpkgs {}, # for a repo that pins nixpkgs with npins
  stdenv ? pkgs.stdenv,
  dep1 ? pkgs.dep1,
  dep2 ? pkgs.dep2,
  ...
}:
stdenv.mkDerivation {
  # derivation defined here which has dependencies on pkgs.dep1 and pkgs.dep2
  # syntactically should be defined using dep1 and dep2
}

Should actually be depending on dep1 and dep2. An important part of using this mechanism correctly is that you never mention pkgs again after the argument list’s default values. Otherwise you can mess some things up, such as splicing for cross-compilation.

1 Like