agentsclimarketplace

Nix module system

Skill YPares/rigup.nix/riglets/nix-module-system

AI agents and Nix: parametrable skills/instructions and tools, packaged together in a reproducible and modular fashionFrom the repository description

Install
npx -y skills add YPares/rigup.nix --skill nix-module-system

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

SKILL.md

4.4 KB, ~1.2k tokens by cl100k_base, as published. Nobody here has run it

Nix Module System: Dark Corners

Practical knowledge about lib.evalModules that's hard to find in official docs.

Sources:

Module Identity & Deduplication

When the same module is included multiple times (e.g., via imports from different places), evalModules deduplicates by identity:

Path-based modules: Deduplicated by path string

modules = [ ./foo.nix ./foo.nix ];  # Same path → evaluated once

Function/attrset modules: Deduplicated by key attribute

# Without key: each inclusion is separate (can cause "defined multiple times" errors)
modules = [ myModule myModule ];  # Evaluated twice!

# With key: deduplicated
myModule = {
  key = "my-unique-module-id";
  imports = [ actualModule ];
};
modules = [ myModule myModule ];  # Evaluated once

Use key when you wrap modules dynamically and need deduplication across import chains.

Module Arguments

_module.args vs specialArgs

Both inject arguments into module functions, but differ in timing:

evalModules {
  specialArgs = { foo = "available during option declaration"; };
  modules = [{
    _module.args = { bar = "only available in config, not options"; };
  }];
}
specialArgs_module.args
Available in options = { ... }
Available in config = { ... }
Can reference config

Rule of thumb: Use specialArgs for things needed to declare options (like lib), use _module.args for runtime values (like pkgs).

_module.check

Disable "unknown option" errors:

{ _module.check = false; }

Useful when modules set options that might not exist (e.g., optional integrations).

_module.freeformType

Allow arbitrary attributes in config without declaring options:

{
  _module.freeformType = lib.types.attrsOf lib.types.anything;

  # Now any attribute is allowed without explicit options
  whatever.you.want = "works";
}

Priority & Merging

mkDefault / mkForce / mkOverride

Control which definition wins when multiple modules set the same option:

# Priority scale: lower number wins
lib.mkOverride 1000 "default priority"    # Same as mkDefault
lib.mkOverride 100 "normal priority"      # Default when no mk* used
lib.mkOverride 50 "force priority"        # Same as mkForce

# Shorthands
lib.mkDefault x  # mkOverride 1000 - easily overridden
lib.mkForce x    # mkOverride 50 - overrides most things

mkMerge

Combine multiple config fragments:

config = lib.mkMerge [
  { services.foo.enable = true; }
  (lib.mkIf condition { services.foo.port = 8080; })
];

mkIf (it's not just if)

lib.mkIf is not the same as Nix's if:

# Nix if: evaluated immediately, fails if option doesn't exist
config = if condition then { foo = 1; } else { };

# lib.mkIf: deferred, only evaluated if condition is true
config = lib.mkIf condition { foo = 1; };

mkIf prevents "infinite recursion" errors when the condition depends on other config values.

mkBefore / mkAfter / mkOrder

For list-type options, control ordering:

{
  environment.systemPackages = lib.mkBefore [ earlyPkg ];  # Prepend
  environment.systemPackages = lib.mkAfter [ latePkg ];   # Append
  environment.systemPackages = lib.mkOrder 500 [ midPkg ]; # Explicit order
}

Disabling Modules

Remove a module from evaluation:

{
  disabledModules = [
    "services/web-servers/nginx.nix"  # Path relative to modules root
    someImportedModule                 # Direct reference
  ];
}

Useful for replacing NixOS modules with custom implementations.

Common Errors & Fixes

See references/troubleshooting.md for detailed error explanations.

Quick fixes:

  • "The option ... is defined multiple times" → Add key attribute or use lib.mkForce/lib.mkMerge
  • "infinite recursion encountered" → Use lib.mkIf instead of if, or check for circular dependencies
  • "The option ... does not exist" → Check spelling, or set _module.check = false for optional deps

What ships with it: 2 files

5.3 KB alongside SKILL.md

references/

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.