passthruIn the Nix ecosystem, we often talk about the purity of derivations. But once you’ve built your software, you frequently need a set of tools to interact with it—scripts for deployment, database migrations, or local development servers.
The traditional way is to define these as separate packages. However,
there’s a powerful “hack” using the let ... in syntax that
allows you to bundle these scripts directly within a package’s
passthru attribute, while allowing them to reference each
other and the package itself.
Imagine you have a derivation pkg that builds a static
site. You want to add a deploy script to its
passthru. That deploy script needs to know the
path to the built site (pkg) and perhaps needs to run an
init script first.
If you try to do this inside stdenv.mkDerivation using
rec { ... }, you’ll quickly hit a wall because
rec doesn’t play well with the way
mkDerivation merges attributes.
letThe solution lies in the fact that let bindings in Nix
are inherently recursive. This means a variable defined in a
let block can reference itself or other variables in the
same block.
By defining your package as a variable in a let block,
you can reference that variable name inside the derivation’s
own definition:
let
pkg = stdenv.mkDerivation {
pname = "my-site";
# ...
passthru = {
init = writeShellScriptBin "init" ''
echo "Initializing infrastructure..."
'';
plan = writeShellScriptBin "plan" ''
# We reference pkg.init here!
${pkg.init}/bin/init
echo "Planning deployment..."
'';
deploy = writeShellScriptBin "deploy" ''
# We reference pkg.plan and the package itself!
${pkg.plan}/bin/plan
cp -r ${pkg} /var/www/html
'';
};
};
in
pkgpassthru scripts as dependencies. If the init
script changes, only the scripts depending on it need to be
updated.passthru of a package tells you exactly how to interact
with it.Another critical use for this pattern is the automated generation of
Software Bills of Materials (SBOMs). By using the self-referential
let, you can bundle SBOM generation tools directly into the
package’s passthru, ensuring they always run against the
exact derivation being built.
I’ve written more about how this simplifies compliance and supply chain integrity in Bill Me Up Boss.
In my own builds, I use this to orchestrate cloud provider
authentication, infrastructure-as-code runs, and static site
deployments—all chained together through the power of a self-referential
let.
Nix allows us to treat our operations as code just as much as our
infrastructure. The recursive let is the glue that holds
that vision together.