Autowiring

An optional autowiring module is provided that will scan the directory structure and wire up the appropriate flake outputs automatically without you having to do it manually.

A ready demonstration is available in nixos-unified-template as well as srid/nixos-config. In the latter, you will notice the following directory structure:

❮ lsd --tree --depth 1 configurations modules overlays packages
📁 configurations
├── 📁 darwin
├── 📁 home
└── 📁 nixos
📁 modules
├── 📁 darwin
├── 📁 flake
├── 📁 home
└── 📁 nixos
📁 overlays
└── ❄️ default.nix
📁 packages
├── ❄️ git-squash.nix
├── ❄️ sshuttle-via.nix
└── 📁 twitter-convert

Each of these are wired to the corresponding flake output, as indicated in the below table:

DirectoryFlake Output
configurations/nixos/foo.nix1nixosConfigurations.foo
configurations/darwin/foo.nix1darwinConfigurations.foo
configurations/home/foo.nix1legacyPackages.${system}.homeConfigurations.foo2
modules/nixos/foo.nixnixosModules.foo
modules/darwin/foo.nixdarwinModules.foo
modules/flake/foo.nixflakeModules.foo
overlays/foo.nixoverlays.foo
packages/foo.nixpackages.${system}.foo3

flake-parts

Autowiring is also provided if you use just flake-parts, via the lib.mkFlake function. In your top-level flake.nix, you only need to define your outputs as follows:

{
  inputs = ...;
  outputs = inputs:
    inputs.nixos-unified.lib.mkFlake
      { inherit inputs; root = ./.; };
}

This will,

  • Auto-import flake-parts modules under either ./nix/modules/flake or ./modules/flake (whichever exists)
  • Use a sensible default for systems which can be overriden.
  • Pass root as top-level module args, as a non-recursive way of referring to the path of the flake (without needing inputs.self).

See srid/haskell-template’s flake.nix for a ready example. For another example, see this emanote PR.

Package Autowiring Example

The packages/ directory allows you to define custom packages that will be automatically wired as flake outputs. Here’s an example project structure:

❮ lsd --tree --depth 2 packages
📁 packages
├── ❄️ hello-world.nix
└── 📁 complex-app
    └── ❄️ default.nix

Each package file should export a function compatible with pkgs.callPackage. Here are two examples:

packages/hello-world.nix - Simple shell script package:

{ lib, writeShellApplication }:

writeShellApplication {
  name = "hello-world";
  text = ''
    echo "Hello from my autowired package!"
    echo "Args: $*"
  '';
  meta = {
    description = "A simple hello world script";
    license = lib.licenses.mit;
  };
}

packages/complex-app/default.nix - Directory-based package:

{ lib, stdenv, makeWrapper }:

stdenv.mkDerivation {
  pname = "complex-app";
  version = "1.0.0";

  src = ./.;

  nativeBuildInputs = [ makeWrapper ];

  installPhase = ''
    mkdir -p $out/bin
    cp app.sh $out/bin/complex-app
    chmod +x $out/bin/complex-app
  '';

  meta = {
    description = "A more complex application";
    license = lib.licenses.gpl3;
    platforms = lib.platforms.unix;
  };
}

After defining these packages, they become available in your flake outputs:

# Build and run packages
nix build .#hello-world
nix run .#complex-app

# List all autowired packages
nix flake show | grep packages

The packages will appear as:

  • packages.${system}.hello-world
  • packages.${system}.complex-app