Change:
Configuration snippet like this: yield a configuration like: However nbd-server expects section to always appear before export sections, otherwise it complains about not being able to find general section. I'd say this is upstream's fault because this order should not matter, but here we are. section to always appear before export sections. <!-- Please @ people who are in the list of the offending package or module. If in doubt, check for whoever last touched something. -- Please run and paste the result.
This looks like it should be easy to fix. I'll probably get around to it tomorrow morning.
For reference, the error looks like this:

Discussion:
let cfg = config.services.nbd; <del> configFormat = pkgs.formats.ini { }; </del> iniFields = with types; attrsOf (oneOf [ bool int float str ]); <del> serverConfig = configFormat.generate "nbd-server-config" ({ generic = (cfg.server.extraOptions // { user = "root"; group = "root"; port = cfg.server.listenPort; } // (optionalAttrs (cfg.server.listenAddress != null) { listenaddr = cfg.server.listenAddress; })); } // (mapAttrs </del> <ins> # The `[generic]` section must come before all the others in the # config file.  This means we can't just dump an attrset to INI # because that sorts the sections by name.  Instead, we serialize it # on its own first. genericSection = { generic = (cfg.server.extraOptions // { user = "root"; group = "root"; port = cfg.server.listenPort; } // (optionalAttrs (cfg.server.listenAddress != null) { listenaddr = cfg.server.listenAddress; })); }; exportSections = mapAttrs </ins> (_: { path, allowAddresses, extraOptions }: extraOptions // { exportname = path; } // (optionalAttrs (allowAddresses != null) { authfile = pkgs.writeText "authfile" (concatStringsSep "n" allowAddresses); })) <del> cfg.server.exports) ); </del> <ins> cfg.server.exports; serverConfig = pkgs.writeText "nbd-server-config" '' ${lib.generators.toINI {} genericSection} ${lib.generators.toINI {} exportSections} ''; </ins> splitLists = partition (path: hasPrefix "/dev/" path)

}; config = mkIf cfg.server.enable { <ins> assertions = [ { assertion = !(cfg.server.exports ? "generic"); message = "services.nbd.server exports must not be named 'generic'"; } ]; </ins> boot.kernelModules = [ "nbd" ]; systemd.services.nbd-server = {

## It's also a loopback device to test exporting /dev/... systemd.services.create-priv-file = mkCreateSmallFileService { path = "/vault-priv.disk"; loop = true; }; <ins> ## `aaa.disk` is just here because "[aaa]" sorts before ## "[generic]" lexicographically, and nbd-server breaks if ## "[generic]" isn't the first section. systemd.services.create-aaa-file = mkCreateSmallFileService { path = "/aaa.disk"; }; </ins> # Needed only for nbd-client used in the tests. environment.systemPackages = [ pkgs.nbd ];

services.nbd.server = { enable = true; exports = { <ins> aaa = { path = "/aaa.disk"; }; </ins> vault-pub = { path = "/vault-pub.disk"; };

if foundString != testString: raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'") server.succeed("nbd-client -d /dev/nbd0") <ins> # Server: Successfully connect to the aaa disk server.succeed("nbd-client localhost ${toString listenPort} /dev/nbd0 -name aaa -persist") server.succeed(f"echo '{testString}' | dd of=/dev/nbd0 conv=notrunc") foundString = server.succeed(f"dd status=none if=/aaa.disk count={len(testString)}")[:len(testString)] if foundString != testString: raise Exception(f"Read the wrong string from nbd disk. Expected: '{testString}'. Found: '{foundString}'") server.succeed("nbd-client -d /dev/nbd0") </ins> ''; })