Curious about system.nix

Hello,
With 26.05 there is this release note about nixos-rebuild having support for a system.nix file:

The system.nix file has been added as an alternative entry point to configuration.nix (and flake.nix) that allows to configure NixOS without using nix-channel

As someone that has almost exclusively used Nix with flakes, I am a bit curious about this. Is this mainly intended for use with pinning frameworks such as niv and nixtamal? What are the pros and cons compared with flakes?

10 Likes

While I do remember the release notes entry, I can’t find anything in the manual :person_shrugging:

Is this mainly intended for use with pinning frameworks such as niv and nixtamal?

Not really, but you can certainly use those now. The main motivation was to make building a NixOS configuration with an empty NIX_PATH possible. It means that you can get rid of nix-channel and declare which Nixpkgs you need to evaluate it. This change also makes nixos-rebuild behave more similarly to nix-build with respect to the --attr flag.

What are the pros and cons compared with flakes?

If you’re already using flakes you probably have no use for this.

While I do remember the release notes entry, I can’t find anything in the manual :person_shrugging:

Yeah, it’s documented in the man pages for nixos-rebuild and nixos-install, but I didn’t have time to write some examples in the NixOS manual.

4 Likes

i used it once. with channels. i didn’t want to use nixpkgs.lib.nixosSystem, i wanted to use a different entry point lib.finixSystem. it worked well in my case.

3 Likes

I think this previous statement by infinisil gives good context to why system.nix is desirable as a means to canonicalize a configuration file that defines a NixOS system (as opposed to the NixOS module) and making it more ergonomic to use with nixos-rebuild

From Pinning configuration.nix with niv - #4 by Infinisil

6 Likes

This system.nix thing was a huge surprise for me. As I haven’t migrated to flakes yet (because they’re not stable), I was intrigued. I’m about 95% done with moving to system.nix; I still have to figure out how to get my standalone Home Manager to see my pinned nixpkgs. I’m calling it with -I in the meantime.

6 Likes

You can’t (automatically), because it’s going to look for a channel called nixpkgs (or more accurately, a path entry in NIX_PATH called nixpkgs), so you’ll need to continue using -I.

The alternative would be setting NIX_PATH globally, if you’re happy with that route.

1 Like

Yeah, this is what I’m leaning towards. With nix.nixPath in my NixOS configuration.nix.

I deal with NIX_PATH like this:

let
  nixpkgs = builtins.fetchTarball { ... };
in
  import "${nixpkgs}/nixos" {
    configuration = {
      imports = [ ./configuration.nix ];
      nix.nixPath = [
        "nixos-config=${toString ./configuration.nix}"
        "nixos-system=${toString ./system.nix}"
        "nixpkgs=/run/current-system/nixpkgs"
      ];
      system.systemBuilderCommands = "ln -s ${nixpkgs} $out/nixpkgs";
    };
  }
  • nixos-config so that nixos-rebuild edit works,
  • nixos-system so I can run nixos-rebuild anywhere,
  • nixpkgs so Nix uses the same Nixpkgs that was used to build the system,
  • and the link to /run/current-system so it doesn’t get gc’ed.
7 Likes

As system.nix now provides a path to specify a NixOS system derivation instead of a module for nixos-rebuild, I think so too would a path need to be implemented to declare a home-manager derivation/activationProfile instead of a module for the home-manager tool.

For now, I think you can “cheat” by making a wrapper that you use to call home-manager with --args to specify the nixpkgs. In other words, the wrapper would call something like home-manager switch --arg pkgs "import (import $(pwd)/npins).nixpkgs {}". If not an npins user, replace the import invocation with something like builtins.fetchTarball or other pinning tool of choice.

Implementation PR: https://github.com/NixOS/nixpkgs/pull/493229

As far as I can tell, this is perhaps still the best way to learn what this thing actually does? Feels useful!

1 Like

Not sure it adds much value on top of the discussion here, but @kiara and I have discussed this feature recently:

4 Likes

You’re pretty much spot on all the times in that discussion.

The fact that configuration.nix is a NixOS module means that (without using nasty hacks) it will never be self-contained, because it doesn’t tell Nix how to build the system that will have said configuration. system.nix does just that, it loads a configuration.nix (if you want, otherwise you can keep everything in the same file) and builds the system derivation.

At this point system.nix is just an extra entry point supported by nixos-install and nixos-rebuild. You can use it already and it works, but for it to take on, and possibly become the default, it needs more integration with other tools and examples in the NixOS manual.

For example:

  • nixos-rebuild --upgrade could update the Nixpkgs archive when using sytem.nix, if we decide on a standard strategy for pinning

  • nixos-generate-config and the installer could have an option to generate system.nix alongside configuration.nix.

  • The nix-channel.nix module should also be extended to handle system.nix and populate nix.nixPath, like it does for a flake-based system (this PR was an older attempt to solve the problem, but part of the changes are still relevant).

I’m very busy with my PhD at the moment, so I can’t work on any of this for now, but I hope someone that finds this useful will put in some work.

11 Likes

Ooh interesting. What would the syntax be for keeping everything in the same file?

3 Likes

@juancnuno has made the good point to me privately that some tools will expect a configuration.nix to exist. I’m going to experiment, because I’m mildly worried about the number of conventions that the nix ecosystem has and would like to find out what breaks in this case.

I’m reasonably sure nixos-option was the one I tried to use

[juancnuno@usscerritos:~]$ nixos-option -I nixpkgs=/nix/store/p7s3mxj2ivmxafdllanz1378y9fj9i8y-source config.programs.bash.enable
error:
       … while evaluating a branch condition
         at /nix/store/gj6hz9mj23v01yvq1nn5f655jrcky1qq-nixos-option.nix:160:1:
          159| in
          160| if !lib.hasAttrByPath path' nixos.config then
             | ^
          161|   throw "Couldn't resolve config path '${lib.showOption path'}'"

       … in the argument of the not operator
         at /nix/store/gj6hz9mj23v01yvq1nn5f655jrcky1qq-nixos-option.nix:160:5:
          159| in
          160| if !lib.hasAttrByPath path' nixos.config then
             |     ^
          161|   throw "Couldn't resolve config path '${lib.showOption path'}'"

       (stack trace truncated; use '--show-trace' to show the full, detailed trace)

       error: file 'nixos-config' was not found in the Nix search path (add it using $NIX_PATH or -I)
1 Like

Yeah the nix ecosystem can be a bit weird because there are a bunch of tools that are not written in nix (because how would they be?), that expect some entry point (default.nix, configuration.nix, flake.nix, home.nix, etc) written in nix, and those entry points are then parsed with nix, potentially manipulated or passed to some other nix function, then something is done with that output.

For the setup linked, the replacement for nixos-option might be something like this using nix repl:

nix repl \
  -I sysexample=https://codeberg.org/juancnuno/nixos/archive/042c06bf5dd3ff1b321156a70f1db9634b30859aebbafce051dbe5b38457462a.tar.gz \
  --expr "import <sysexample/system.nix>"

Nix 2.34.7
Type :? for help.
Loading installable ''...
Added 6 variables.
config, options, pkgs, system, vm, vmWithBootLoader

nix-repl> options.programs.bash.enable
{
  __toString = «lambda __toString @ /nix/store/6y8gb3ddc5nh9dx3jn51xsgq97agk6kl-source/lib/modules.nix:1157:20»;
  _type = "option";
  declarationPositions = [ ... ];
  declarations = [ ... ];
  default = true;
  definitions = [ ... ];
  definitionsWithLocations = [ ... ];
  description = "Whenever to configure Bash as an interactive shell.\nNote that this tries to make Bash the default\n{option}`users.defaultUserShell`,\nwhich in turn means that you might need to explicitly\nset this variable if you have another shell configured\nwith NixOS.\n";
  files = [ ... ];
  highestPrio = 1500;
  isDefined = true;
  loc = [ ... ];
  options = [ ... ];
  type = { ... };
  value = true;
  valueMeta = { ... };
}

nix-repl> :p options.programs.bash.enable.definitionsWithLocations
[
  {
    file = "/nix/store/6y8gb3ddc5nh9dx3jn51xsgq97agk6kl-source/nixos/modules/programs/bash/bash.nix";
    value = true;
  }
]

It’s a bit less nicely formatted than nixos-option, but also via nix repl 1) it’s more flexible about what info is available and 2) you get tab-completion. The --expr "import <sysexample/system.nix>" part of the cmdline basically inherits everything in the attrset defined by the argument. So in this case things like options and config, etc

1 Like

I’ve been using nixos-rebuild repl as a fallback

2 Likes

If you’re building NixOS by importing "${nixpkgs}/nixos" you can pass as the configuration argument a filepath or directly a NixOS module (which contains your configuration).

For example, here I’m actually doing both, since you can use imports to load more files:

Yes, this another tool that needs some integration.

Just do nixos-rebuild repl, which works with system.nix.

1 Like