Library / SDK
nix-community/nix-ld avatar
nix-community/nix-ld

nix-ld: Running Unpatched Binaries on NixOS

Run unpatched dynamic binaries on NixOS [maintainer=@Mic92]

1,728 stars27 forksRustMIT

At a glance

What is it?
nix-ld bridges the hardcoded ELF loader paths used by precompiled Linux binaries to loaders and libraries stored in the Nix store. It is useful when patching executables is impossible or undesirable, but it does not configure interpreters that already use Nix store loaders.
Who is it for?
Adopt nix-ld when a trusted executable hardcodes /lib64/ld-linux-x86-64.so.2 and patching it would be undesirable or impractical. Do not use it as a general library environment for Python, Node.js, Ruby, or other Nix-built interpreters, because those binaries bypass the shim.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why nix-ld Exists for Precompiled Linux Executables

nix-ld addresses a specific compatibility gap on NixOS. A binary compiled for a conventional Linux distribution often contains a hardcoded interpreter path such as /lib64/ld-linux-x86-64.so.2. NixOS normally keeps its dynamic linker inside a glibc package in the Nix store, so that hardcoded path does not lead to a usable loader. This often affects proprietary packages and software acquired through third-party package managers rather than built through nixpkgs.

The project is aimed at users who need to run existing software while preserving its downloaded form. The README identifies executables from tools such as VS Code, npm, or pip as examples. It also names games and proprietary programs that check file integrity, plus FPGA IDEs that may be too large for the Nix store. For those cases, patching the executable at each update can be inconvenient or conflict with integrity checks.

The normal nixpkgs solution is to patch a package so its interpreter and library references match NixOS. nix-ld addresses cases where that is not possible. It does not make every conventional executable portable, however. Its compatibility depends on the loader path baked into the binary and on finding a compatible loader plus the libraries required by that binary.

The Loader Shim and Environment Data Flow

The NixOS module installs nix-ld at the location used by common Linux x86-64 distributions for the glibc loader. According to the README, this path is /lib64/ld-linux-x86-64.so.2. When an unpatched executable invokes that path, the kernel loads nix-ld instead of opening the expected glibc file directly.

The shim then consults NIX_LD, which identifies the actual dynamic loader in the Nix store. This separates the fixed path compiled into the executable from the real Nix package selected by the environment. The project also supports system-specific forms such as NIX_LD_x86_64_linux. In that name, the system component has the Nix system value's dashes replaced with underscores.

Library selection follows a separate path. NIX_LD_LIBRARY_PATH accepts a colon-separated list, which nix-ld converts into LD_LIBRARY_PATH before transferring execution to the selected loader. A system-specific NIX_LD_LIBRARY_PATH_x86_64_linux form is also supported. This indirection is important because setting LD_LIBRARY_PATH globally could affect correctly built Nix applications whose RPATH settings would otherwise select compatible libraries.

The default Cargo feature is named entry_trampoline. Its description says it reverts nix-ld's LD_LIBRARY_PATH changes before executing the real program. Logging is available through NIX_LD_LOG, with error, warn, info, debug, and trace levels. These controls make it possible to narrow environment configuration to a shell, wrapper, or particular class of executables instead of applying one library set to every process on the system.

Installing nix-ld Through nixpkgs or a Flake

The README states that nix-ld is part of nixpkgs since NixOS 22.05. For a system using that packaged module, add this option to the NixOS configuration:

nix
{
  programs.nix-ld.enable = true;
}

This enables the nixpkgs module, which installs the loader shim at the expected conventional Linux location. Existing NixOS configuration must then be rebuilt and activated through the user's normal system workflow. The README does not provide host-specific rebuild commands, so those commands are not added here.

For a flake-based configuration, the documented approach adds nix-ld as an input and includes its NixOS module. The configuration below is the README's structure, with myhostname remaining the reader's placeholder:

nix
{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/master";
  inputs.nix-ld.url = "github:Mic92/nix-ld";
  inputs.nix-ld.inputs.nixpkgs.follows = "nixpkgs";

  outputs = { nix-ld, nixpkgs, ... }: {
    nixosConfigurations.myhostname = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        nix-ld.nixosModules.nix-ld
        { programs.nix-ld.dev.enable = true; }
      ];
    };
  };
}

The repository version defines programs.nix-ld.dev.enable rather than programs.nix-ld.enable. The README explains that the separate namespace avoids a collision with the nixpkgs module. This distinction matters when following an older example that enables the nixpkgs package and a newer repository example.

A First Executable Run in a nix-shell

After the nix-ld symlink is configured, an executable needs both the real loader and the libraries it expects. The README provides a shell.nix pattern that constructs these values from packages in the current Nix shell. Here, stdenv.cc.cc supplies compiler-runtime libraries, while openssl represents an application dependency:

nix
with import <nixpkgs> {};
mkShell {
  NIX_LD_LIBRARY_PATH = lib.makeLibraryPath [
    stdenv.cc.cc
    openssl
    # ...
  ];
  NIX_LD = stdenv.cc.bintools.dynamicLinker;
}

Entering that nix-shell makes both variables available to an unpatched executable launched from the shell. If the binary requests /lib64/ld-linux-x86-64.so.2, nix-ld receives that request, reads NIX_LD, and transfers the launch to the selected dynamic linker. The library list is passed through the controlled LD_LIBRARY_PATH conversion described by the project.

Finding the initial library list can require inspection. The README says to use ldd on the binary or set LD_DEBUG=libs. It also points to nix-autobahn, nix-alien, and nix-index as tools for different parts of this discovery process. The exact expected result depends on the executable. A program may start without errors, fail while reporting a missing shared object, or crash because an available library has an incompatible ABI. That is why the library list should reflect the target program rather than every library available in the shell.

Interpreter and Library Discovery Boundaries

nix-ld does not become a general library search mechanism for every Nix process. Its shim participates only when an executable invokes a conventional loader location under /lib or /lib64. A Python or Node.js interpreter built by nixpkgs is normally configured to use a glibc from the Nix store, so it will not consult NIX_LD or NIX_LD_LIBRARY_PATH merely because those variables are set.

That boundary matters for virtual environments. A pip package can contain native extensions, but setting NIX_LD_LIBRARY_PATH for an unpatched executable does not automatically configure the Nix-built Python interpreter that loads those extensions. The documented remedy is a wrapper that converts NIX_LD_LIBRARY_PATH into LD_LIBRARY_PATH only for the intended interpreter process:

nix
(pkgs.writeShellScriptBin "python" ''
  export LD_LIBRARY_PATH=$NIX_LD_LIBRARY_PATH
  exec -a "$0" ${pkgs.python3}/bin/python "$@"
'')

The exec -a "$0" detail is required by the README. Without it, the wrapper can be skipped, which would leave the intended interpreter replacement behavior ineffective. Creating a separate pythonld command is another documented option when developers do not want to export the library path for their main Python command.

This design is a trade-off. nix-ld avoids globally injecting libraries into correctly built Nix applications, but it also means users must understand whether a process enters through the shim or already has a store-based interpreter. Native package discovery is therefore an operational concern rather than something nix-ld resolves automatically.

Trade-offs Against FHS Wrappers and Patching

The closest documented alternatives are patching, buildFHSUserEnv, nix-alien-ld, and nix-autobahn-ld. Patching changes the executable itself so its interpreter and library references match Nix. That produces a conventional Nix derivation when a maintained package can accept the patch, but it requires rebuilding or modifying software at update time. nix-ld leaves the downloaded executable untouched, which is useful for automatic update channels and integrity checks.

buildFHSUserEnv takes a different approach by restoring a conventional Linux directory hierarchy inside a sandbox. The README identifies concrete limitations: setuid binaries cannot execute there, and other sandbox tools such as bwrap or nix build cannot be used inside it. It also requires a subshell, which does not work well with direnv. nix-ld instead supplies the loader shim directly at the path expected by the executable, without requiring that application to run inside the documented fhsuserenv shell.

nix-alien-ld uses another mechanism. It wraps the program with writeShellScriptBin and sets NIX_LD and NIX_LD_LIBRARY_PATH around that wrapper. nix-ld does not provide the same automated wrapping workflow. nix-autobahn-ld automates generation of shell expressions and may reduce the manual library configuration.

There are cases where nix-ld is the wrong tool. It should not replace a normal Linux system loader, because doing so can break that distribution. It also does not configure Nix-built interpreters that already point into the Nix store. For software that is small, reproducible, and routinely built in nixpkgs, autoPatchelfHook remains the less environmentally dependent approach.

Release Cadence, Repository Modules, and MIT Terms

The repository is not archived, and its last push was on 2026-10-13. The documented release history includes version 2.0.6 dated 2025-10-01, version 2.0.5 dated 2025-07-15, and version 2.0.4 dated 2024-12-27. Cargo.toml identifies the current package version as 2.0.6 and uses Rust edition 2024. It also exposes a nix-ld binary and a tests target.

The repository includes modules, NixOS tests, examples, scripts, and files for building or packaging. This layout supports more than the simple README installation path. The examples directory includes masterpdfeditor.nix and zoom.nix, while the justfile defines tests across x86_64, i686, and aarch64 Linux targets. Those files indicate that upgrade work involves both the Rust executable and its NixOS integration, not only the loader shim itself.

The repository identifies the MIT license. The LICENSE file is the authoritative source for its exact terms and any notices that must accompany redistribution or modification. This article does not interpret those legal obligations. An adopter incorporating nix-ld source should inspect the current LICENSE file and assess how its module and packaging changes fit into the target NixOS configuration. Because repository and nixpkgs installations use different option names, an upgrade review should also check whether an existing configuration still selects the intended module.

Editorial conclusion

Adopt nix-ld when a trusted executable hardcodes /lib64/ld-linux-x86-64.so.2 and patching it would be undesirable or impractical. Do not use it as a general library environment for Python, Node.js, Ruby, or other Nix-built interpreters, because those binaries bypass the shim. Verify first that the executable hardcodes the supported loader path, identify its required libraries, and confirm that the selected glibc or other runtime is compatible with the program.

Frequently asked questions

How do I install and enable nix-ld on NixOS?

The README says nix-ld is available in nixpkgs since NixOS 22.05. Set programs.nix-ld.enable to true, or add nix-ld as a flake input and enable programs.nix-ld.dev.enable for the repository module.

How do I run an unpatched executable with nix-ld?

Set NIX_LD to the dynamic loader in the Nix store and NIX_LD_LIBRARY_PATH to a colon-separated list of required libraries. The executable must invoke a loader path covered by the installed shim, such as /lib64/ld-linux-x86-64.so.2.

Does nix-ld configure Python and Node.js libraries?

Not automatically. Nix-built interpreters already use a loader from the Nix store and bypass nix-ld, so their native-library search path may need LD_LIBRARY_PATH or a project-specific wrapper.

Official sources

  1. Issues
  2. License: MIT
  3. nix-community/nix-ld on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/nix-community-nix-ld.svg)](https://hysenlabs.com/projects/nix-community-nix-ld)

Was this useful?