zerforschen.plus/content/posts/why-i-do-not-use-flake-utils.md
2025-04-07 18:25:01 +02:00

4.2 KiB
Raw Blame History

+++ date = '2025-04-06T14:49:03+02:00' draft = false title = 'Why I do not use flake-utils' tags = ['nix'] +++

I have been using [Nix] for a while now. Around a year ago, I switched everything from the [servicepoint] library to my [machine configuration] over to flakes.

For me the biggest advantage flakes bring is not additional functionality. Instead, they are an easier and semi-standardized way to do what you could before.

When learning flakes, you often see [flake-utils] being used. With it, you can shorten your flakes by not having to specify everything per system.

Without anything

{
  description = "Flake utils demo - without flakes";

  outputs = { self, nixpkgs, flake-utils }:
  {
    packages."x86_64-linux" = rec {
          hello = pkgs.hello;
          default = hello;
    }
    packages."aarch64-linux" = rec {
          hello = pkgs.hello;
          default = hello;
    }

    # more systems ...
  }
}

With flake-utils

{
  description = "Flake utils demo";

  inputs.flake-utils.url = "github:numtide/flake-utils";

  outputs = { self, nixpkgs, flake-utils }:
    flake-utils.lib.eachDefaultSystem (system:
      let pkgs = nixpkgs.legacyPackages.${system}; in
      {
        packages = rec {
          hello = pkgs.hello;
          default = hello;
        };
      }
    );
}

With function in flake

To make a long story short, here is what I usually do instead:

{
  description = "forAllSystems demo";

  outputs = { self, nixpkgs }:
    let
      supported-systems = [ # [1]
        "x86_64-linux"
        "aarch64-linux"
      ];
      forAllSystems =
        f:
        nixpkgs.lib.genAttrs supported-systems /* [2] */ (
          system:
          f rec {
            pkgs = nixpkgs.legacyPackages.${system}; # [3]
            inherit system;
          }
        );
    in
    {
      packages = forAllSystems ({ pkgs, ... }: rec {
        hello = pkgs.hello;
        default = default;
      });
    }
}

Thats definitely more code! - Yes, but it also includes more information in the flake, while getting rid of an external dependency. While more code can be intimidating for beginners, it actually helps remove a barrier to understanding how the flake works in this case. For me, it wasnt a problem to ignore boilerplate like this at first, slowly learning more language features until I finally understood everything.

At [1], the supported systems are specified. I personally use x86_64-linux and aarch64-linux, but I also usually support x86_64-darwin and aarch64-darwin in public projects. If you want to support any system, you can use [nixpkgs.lib.system.flake-exposed] at [2] instead of defining your own list.

Because the definition is right inside the flake, you can tweak what gets passed to the function. For example, the [flake for RedoxOS-development] I contributed1 this to passes the custom rust-toolchain. An example for how to do it is already right there: at [3], pkgs is provided.

Another possible tweak: You may want to define separate supported systems for each output. This is useful, for example, if the target environment you're developing for cannot support a development shell.

For me, the trade-offs are worth it, as they provide greater transparency and control over the flake configuration.

That being said, I fully acknowledge that flake-utils can still be a great choice for many people. It simplifies things and reduces the need to write boilerplate code, which can be a big plus depending on your needs and workflow. Ultimately, it's a matter of personal preference and the specific requirements of your project.


  1. If you check the history, you will see I am not mentioned. I am still a bit salty about that, as it was my first contribution to a bigger OSS project. [Nix]: https://nixos.org/ [servicepoint]: https://git.berlin.ccc.de/servicepoint/servicepoint [machine configuration]: https://git.berlin.ccc.de/vinzenz/nixos-configuration [flake-utils]: https://github.com/numtide/flake-utils [nixpkgs.lib.system.flake-exposed]: 374e6bcc40/lib/systems/default.nix (L58) [flake for RedoxOS-development]: cb34b9bd86/flake.nix (L38) ↩︎