Making devenv start fast, and the whole nixpkgs with it - devenv Devenv startup overhead is traced to Nixpkgs' dynamic linker stat storm, where programs like `devenv hook-should-activate` suffer 70ms delays from 486 failing `openat()` calls per invocation. The Nix package manager's per-dependency `DT_RUNPATH` design forces the loader to search dozens of directories for each shared library, a problem that has remained unfixed for a decade. The article explores potential fixes, including static linking, to eliminate the dynamic loader entirely. Making devenv start fast, and the whole nixpkgs with it I'm sitting here next to Farid Zakaria https://github.com/fzakaria at Tacosprint https://tacosprint.org where we looked at the stat storm that has been haunting nixpkgs for a decade. devenv auto activation ../../../../../auto-activation/ runs devenv hook-should-activate on every shell prompt to decide whether you've stepped into a project directory. It does almost nothing: discover the project, check the trust database, print a path. So its runtime is pure startup overhead, and it runs on every single prompt redraw. bash $ time devenv hook-should-activate /home/domen/dev/myproject real 0m0.070s ... 70ms before a prompt, every prompt. And this isn't devenv's tax to pay, it's nixpkgs'. Every program pays it before it runs a line of its own code: the dynamic loader has to find each shared library, and the way Nix scatters packages across the store makes that search slow. This is not news. The cost has been measured, written up, and partly fixed more than once, and yet it has sat in limbo for the better part of a decade with no general fix merged into nixpkgs. Most of that is the dynamic loader looking for a shared object that is sitting right there in the store, just not in the first directory it tried. The loader knocks on 486 wrong doors before it finds the right ones, and almost all of it happens before main even starts. That number is the whole game. Above ~30ms you have to bolt a caching layer on top of the hook; in single digit milliseconds you just run it on every prompt and throw the cache away. And it scales with the closure: imagemagick 's magick --version makes 1225 failing opens: bash $ strace -f -e openat magick --version 2 &1 /dev/null | grep '\.so' | grep -c ENOENT 1225 The community has been circling a real fix for years. This post walks through the problem, the approaches people have tried with their tradeoffs, and a more radical one we spiked for devenv to see if it was even possible: deleting the dynamic loader altogether by linking the whole program into one static binary. The umbrella tracking issue for the general problem is NixOS/nixpkgs 481620 https://github.com/NixOS/nixpkgs/issues/481620 . Why Nix makes the loader work so hard On a traditional distribution every shared library lives in a handful of global directories such as /usr/lib . The dynamic loader has a short, mostly cached search path, and ld.so.cache built by ldconfig turns soname lookups into a hash table hit. Nix is different by design. Every package lives in its own /nix/store/