Is it possible to have a config.system.{...} overlay?

Hi!

I’ve wanted to modify the GRUB install script for my installation (automatic signing of files, cosmetic changes in configuration names etc) but I’ve stumbled into an issue I cannot solve.

The installation hook is defined in system.build.installBootLoader in nixpkgs, precisely at this line:

As you can see the actual perl script is defined in a let, which does not easily make it replaceable. That’s not a huge deal though, I’ve redefined my own in a let block which gathers nixpkgs’s from GitHub and applies a few patches to it.

Once this is done I’d need to replace the perl script inside installBootLoader.
I’ve already written an overrideAttrs which can do that inline but the issue is I am now dependent on the old installBootLoader as it is a bit more than a perl wrapper, namely it calls it with an xml generated in a let (urgh again) which contains all the arguments passed to the grub module and I’d rather avoid having to redefine the entire grub.nix if possible.

This seems like the perfect usage for overlays, but I couldn’t figure out for the love of all that exists if it is even possible to overlay something in system.

The overlay definition would look akin to something like that:

          system.build.installBootLoader = super.system.build.installBootLoader.overrideAttrs (oldAttrs: {
          # this absolute monstrosity is written within builtins and basically
          # splits a string before and after .*pl and puts it back together but
          # with a custom perl script
          # basically, in a pseudo language that makes sense:
          # split = split_text(text, ".*/bin/perl")
          # text = split[1][0] + " ${install-grub-pl} " + split[4]
          text = (builtins.elemAt (builtins.elemAt (builtins.split
          "(^.*/bin/perl)|( .*\\.pl) " oldAttrs.text) 1) 0) + " ${install-grub-pl} "
          + builtins.elemAt (builtins.split "(^.*/bin/perl)|( .*\\.pl) "
          oldAttrs.text) 4;
       });

I switched to builtins only to avoid making a new package definition while trying to get this working, any cleaner way to do that would be very much appreciated.

Any clue on how I should go around at solving this issue with minimal overriding? I’d much rather avoid having my own nixpkgs fork and/or grub fork for this specific use case.

You can override NixOS modules by excluding that path from being imported, and then importing your overriding module in your configuration:

{ config, pkgs, ... }:
{
  disabledModules = [ "system/boot/loader/grub/grub.nix" ];
  imports = [
    <your replacement module>
  ];
}

I’m using this strategy to replace my bootloader module: ~mchrist/dotfiles (master): nixos/modules/secureboot.nix - sourcehut git
(Though it did require copy-pasting a bit of stuff from the original bits in nixpkgs.)

This isn’t quite as ergonomic as an nixpkgs overlay where you can inherit the packages original attributes, but I hope it helps. Maybe there’s some way to make this more like an override…

Argh I would have preferred avoiding resorting to this since I need to keep the module updated but this does indeed solve the issue, thanks @mattchrist !

We should probably think about having a higher level overlay type to overwrite such settings though, should I open an issue or am I missing something that would make an unwanted feature?

I have made a PR which allows you to run shell scripts after the bootloader has been installed and the menu entries generated, this should take care of most use cases I’m aware of GRUB-wise.
https://github.com/NixOS/nixpkgs/pull/108242

2 Likes