Overview
Dynamic discovery is the mechanism that automatically finds and builds configurations without requiring manual imports. Instead of maintaining lists of systems and users, the flake scans directories and constructs configurations on the fly.The Problem
Traditional Nix flake configurations require explicit imports:- Adding a new system requires updating the flake
- Easy to forget to add new configurations
- Boilerplate scales linearly with number of systems
- Refactoring is tedious
The Solution
This homelab uses discovery functions that scan directories and build configurations automatically:- Add a new system → instantly available
- Zero boilerplate maintenance
- Scales to hundreds of systems
- Refactoring is trivial
Discovery Functions
The discovery logic lives inlib/default.nix and provides three main functions.
discover - Configuration Discovery
Purpose: Finds directories with default.nix and standalone .nix files.
Source code from lib/default.nix:4-14:
- Read directory:
builtins.readDir dirreturns an attribute set of{ name = type; } - Filter entries: Keep only:
- Directories containing
default.nix - Regular
.nixfiles (exceptdefault.nixitself)
- Directories containing
- Transform names: Remove
.nixsuffix from filenames - Build paths: Create full paths to configurations
discover function returns:
discoverTests - Test Discovery
Purpose: Specifically designed to find and evaluate test files.
Source code from lib/default.nix:17-26:
discover:
- Only finds regular
.nixfiles (no directories) - Immediately imports and evaluates each file
- Passes
argsto each test withliboverride
readMeta - Metadata Reader
Purpose: Reads optional meta.json files from configuration directories.
Source code from lib/default.nix:29-33:
- Checks if
meta.jsonexists in the given path - If yes: parses JSON and returns attribute set
- If no: returns empty set
{}
flake.nix:233-235:
The
or operator provides a default value when system is not specified in meta.json.Discovery in Practice
Systems Discovery
Fromflake.nix:348:
lib.discover ./systems→{ server = ./systems/server; ... }lib.mapAttrs mkSystem→ CallsmkSystem "server" ./systems/serverfor each- Result:
{ server = <nixosSystem>; ... }
Droids Discovery
Fromflake.nix:351:
Homes Discovery
Home Manager uses custom discovery logic to handle the three naming patterns. Fromflake.nix:355-374:
allEntries={ alice = "directory"; alice@global = "directory"; ... }homeDirs=[ "alice" "alice@global" "alice@workstation" "bob" ]validUsers=[ "alice" "alice@global" "bob" ](removedalice@workstation)usernames=[ "alice" "bob" ](normalized and deduplicated)- Final:
{ alice = <homeConfiguration>; bob = <homeConfiguration>; }
The
alice@workstation directory is intentionally excluded from homeConfigurations because it’s meant to be imported by the NixOS system configuration, not used standalone.The mkHome Builder
From flake.nix:166-204:
- Checks if both
alice/andalice@global/exist - Combines them into a single configuration
- Uses
lib.optionalto conditionally include modules - Respects architecture from metadata
Extending Discovery
Adding a New System Type
To add discovery for a new configuration type:- Create a builder function:
- Apply discovery:
- Export in flake:
Custom Discovery Functions
You can create specialized discovery for unique patterns:Benefits
Reduced Cognitive Load
No need to remember to update imports. Just create a directory and it’s automatically available.Scalability
The approach handles 1 system as easily as 100 systems:- Same amount of code
- Same performance characteristics
- Same mental model
Refactoring Freedom
Renaming is as simple as renaming the directory:Type Safety
Discovery still maintains Nix’s evaluation guarantees:nix flake checkvalidates all discovered configs- Type errors caught at evaluation time
- No “hidden” configurations
Inspection
You can see what was discovered:Summary
Dynamic discovery provides:- Automation - Configurations found automatically
- Simplicity - No import boilerplate
- Scalability - Handles growth effortlessly
- Flexibility - Supports multiple patterns
- Safety - Still validated by
nix flake check
discover, discoverTests, and readMeta—work together to create a self-organizing configuration system that grows with your infrastructure.