Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Fragment Directories

The .d pattern enables composable configuration where multiple sources contribute to a single output. This is particularly useful when external tools inject files into your project.

Auto-merged directories

Any directory ending in .d is automatically merged by imp.tree:

outputs/
  perSystem/
    packages.d/
      00-core.nix       # { default = myPkg; foo = fooPkg; }
      10-extras.nix     # { bar = barPkg; }
    devShells.nix

Result:

self'.packages = { default = myPkg; foo = fooPkg; bar = barPkg; }

Files are processed in sorted order (00 before 10) and merged with lib.recursiveUpdate. Later files can override or extend earlier ones.

This works for any name - packages.d/, myCustom.d/, utils.d/, etc.

Shell hook fragments

For .d directories containing non-.nix files (like shell scripts), use imp.fragments:

{ pkgs, imp, ... }:
let
  shellHookFragments = imp.fragments ./shellHook.d;
in
{
  default = pkgs.mkShell {
    shellHook = shellHookFragments.asString;
  };
}

Common patterns:

DirectoryContentCollection method
shellHook.d/Shell scripts (.sh)imp.fragments.asString
env.d/Environment attrsetsimp.fragmentsWith.asAttrs

Merging base files with fragments

When both foo.nix and foo.d/ exist for a mergeable output, they are combined:

outputs/
  packages.nix        # { default = myPkg; }
  packages.d/
    10-extras.nix     # { bar = barPkg; }

Result:

self'.packages = { default = myPkg; bar = barPkg; }

The base file (foo.nix) is imported first, then fragments from foo.d/ are merged on top using lib.recursiveUpdate. This allows a base file to define core outputs while fragments extend them.

Fragment file structure

Fragments can be *.nix files, *.sh scripts, or directories with a default.nix or package.nix entry point. Directory fragments can bundle additional resources alongside the entry point.

Each .nix fragment can use the standard patterns:

# Simple attrset
{ default = myPkg; }

# Function receiving args
{ pkgs, ... }:
{ default = pkgs.hello; }

# With __inputs for flake input collection
{
  __inputs.foo.url = "github:owner/foo";
  __functor = _: { pkgs, foo, ... }: { default = foo.packages.${pkgs.system}.bar; };
}

Use case: injected dependencies

The .d pattern shines when external tools add files to your project:

outputs/perSystem/
  packages.d/
    00-rust.nix         # Your rust packages
    10-tools.nix        # Injected by external tool
  devShells.nix         # Your main devShell using inputsFrom
  devShells.d/
    10-tools.nix        # Injected devShell fragment

Your packages.d/00-rust.nix defines the main packages. The injected 10-tools.nix adds extra commands. Both are merged into self'.packages without any manual wiring.

For devShells, injected fragments in devShells.d/ provide composable shells that your devShells.nix can consume via inputsFrom:

# devShells.nix
{ pkgs, self', ... }:
{
  default = pkgs.mkShell {
    inputsFrom = [ self'.devShells.tools ];  # from devShells.d/
    packages = [ /* your packages */ ];
  };
}

This uses the standard Nix inputsFrom pattern for shell composition.