Defining OS as flake, Raspberry Pi 4

Hi! I’m (mostly) totally new to the world of Nix/NixOS, and as trying to learn it I’m working on setting up Raspberry Pi’s to host different software. On my journey I’ve stumbled across flakes and figured it’d be good to learn to use flakes to build the OS. I’ve run into issues doing this though and haven’t been able to figure out the errors so far.

The configuration.nix file I have (working) is as follows:

{ config, pkgs, lib, ... }:

let
  hostname = "xxxxx";
  user = "xxxxx";
  password = "xxxxx";
  nixosHardwareVersion = "7f1836531b126cfcf584e7d7d71bf8758bb58969";

  timeZone = "America/New_York";
  defaultLocale = "en_US.UTF-8";
in {
  imports = ["${fetchTarball "https://github.com/NixOS/nixos-hardware/archive/${nixosHardwareVersion}.tar.gz" }/raspberry-pi/4"];

  fileSystems = {
    "/" = {
      device = "/dev/disk/by-label/NIXOS_SD";
      fsType = "ext4";
      options = [ "noatime" ];
    };
  };

  networking.hostName = hostname;

  environment.systemPackages = with pkgs; [
    curl
    wget
    vim
    git
  ];

  services.openssh.enable = true;

  time.timeZone = "America/New_York";

  i18n = {
    defaultLocale = defaultLocale;
    extraLocaleSettings = {
      LC_ADDRESS = defaultLocale;
      LC_IDENTIFICATION = defaultLocale;
      LC_MEASUREMENT = defaultLocale;
LC_MONETARY = defaultLocale;
      LC_NAME = defaultLocale;
      LC_NUMERIC = defaultLocale;
      LC_PAPER = defaultLocale;
      LC_TELEPHONE = defaultLocale;
      LC_TIME = defaultLocale;
    };
  };

  users = {
    mutableUsers = false;
    users."${user}" = {
      isNormalUser = true;
      password = password;
      extraGroups = [ "wheel" ];
    };
  };

  # Enable passwordless sudo.
  security.sudo.extraRules= [
    {  users = [ user ];
      commands = [
         { command = "ALL" ;
           options= [ "NOPASSWD" ];
        }
      ];
    }
  ];

  # Enable GPU acceleration
  hardware.raspberry-pi."4".fkms-3d.enable = true;

  hardware.pulseaudio.enable = true;
  nix.settings.experimental-features = [ "nix-command" "flakes" ];
  system.stateVersion = "23.11";
}

the flake.nix file I have been trying (not working) is as follows:

# This can be built with nixos-rebuild --flake .#myhost build
{
  description = "the simplest flake for nixos-rebuild";

  inputs = {
    nixpkgs = {
      # Using the nixos-unstable branch specifically, which is the
      # closest you can get to following the equivalent channel with flakes.
      url = "github:NixOS/nixpkgs/nixos-unstable";
    };
  };

  # Outputs can be anything, but the wiki + some commands define their own
  # specific keys. Wiki page: https://nixos.wiki/wiki/Flakes#Output_schema
  outputs = { self, nixpkgs }: {
    # nixosConfigurations is the key that nixos-rebuild looks for.
    nixosConfigurations = {
      myhost = nixpkgs.lib.nixosSystem {
        system = "aarch64-linux";
        # Import our old system configuration.nix
        modules = [
          ./configuration.nix
        ];
      };
    };
  };
}

Both files are in /etc/nixos. Running sudo nixos-rebuild --flake .#myhost switch gives me the following errors:

error:
       … while calling the 'seq' builtin

         at /nix/store/cz973qsjldbw4x7fx0rwcvhr9k645vz2-source/lib/modules.nix:320:18:

          319|         options = checked options;
          320|         config = checked (removeAttrs config [ "_module" ]);
             |                  ^
          321|         _module = checked (config._module);

       … while evaluating a branch condition

         at /nix/store/cz973qsjldbw4x7fx0rwcvhr9k645vz2-source/lib/modules.nix:261:9:

          260|       checkUnmatched =
          261|         if config._module.check && config._module.freeformType == null && merged.unmatchedDefns != [] then
             |         ^
          262|           let

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

       error: in pure evaluation mode, 'fetchTarball' requires a 'sha256' argument

Not entirely sure where to go from that, I can see some examples in the docs about passing a sha256 argument to this, but they don’t have a similar format to my own config file.
Any help would be appreciated :slight_smile:

We can solve this problem in a few ways. First, let’s start with the most direct solution to your problem, but keep in mind, this isn’t the best way:

error: in pure evaluation mode, 'fetchTarball' requires a 'sha256' argument

“Pure evaluation mode” is a good thing. It means that your build is “pure”, only using the source of your Flake to build it. It will ignore environment variables, other files on your filesystem, that sort of thing. Definitely stay in pure evaluation mode.

In pure evaluation mode, every input to your build needs to be “pinned” so that the build is reproducible. In this case, fetchTarball will make a web request, which could (in principle) return different payloads for different people. So, this message is asking you to specify a hash of the payload that you expect.

An easy way to compute the hash is to add an empty sha256 argument:

{
  imports = ["${fetchTarball {
    url = "https://github.com/NixOS/nixos-hardware/archive/${nixosHardwareVersion}.tar.gz" }/raspberry-pi/4";
    sha256 = "";
  ];
}

If you build this, you will get another error message telling you that the given hash (empty) mismatches the actual hash, and it will tell you what that hash is. Put that in, and your build should succeed.


Okay, so assuming you’ve done that and it worked, that was the wrong way to solve this problem (but it was a useful learning experience). The right way is to leverage the main strength of flakes: pinning inputs.

In your flake.nix, you already have an inputs definition with nixpkgs in it. Add another definition, just like the nixpkgs one, this time for nixos-hardware:

{
  nixos-hardware = {
    url = "github:NixOS/nixos-hardware";
  };
}

Then, where you reference configuration.nix in your flake, additionally reference the nixos-hardware module:

modules = [
  ./configuration.nix
  nixos-hardware.nixosModules.raspberry-pi-4
];

You will also need to add nixos-hardware to the function arguments on the outputs = { self, nixpkgs }: line.

With that, you can remove the imports line from configuration.nix, and everything should still build.


But there’s one more thing! Your configuration.nix has a nixosHardwareVersion string. Where does that go?

With flakes, all the input versions are “pinned” using the flake.lock file. You can see these versions with nix flake metadata and update them to the latest with nix flake update. If you want a specific version, you can configure that in your flake.nix (if you want to permanently pin it) or in flake.lock (if you want to pin a certain version for now until you next update).


Hope that helps!

3 Likes

Thank you so much for your detailed and patient reply! That all worked splendidly, exactly as you described :slight_smile:
This taught me a lot about Nix and NixOS! Looking forward to exploring more