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
defines how to interact with the package.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 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.