As shown under another article, I’m using uvx to run some mcp servers needed by mcp.el. However, when I first started them on NixOS, I encountered an error. After consulting Gemini for solutions and a root cause analysis, here are my notes.

Symptoms and Error Message

Checking the *Mcp-Hub/ buffer with mcp-hub-view-log, the following error appeared:

[stderr]  Could not start dynamically linked executable: /home/bk/.cache/uv/archive-v0/unI5q9QapqXHm9fPXna4G/bin/python
[stderr]  NixOS cannot run dynamically linked executables intended for generic
[stderr]  linux environments out of the box. For more information, see:
[stderr]  https://nix.dev/permalink/stub-ld
[jsonrpc] D[14:56:49.461] Connection state change: `exited abnormally with code 127

Solution: Enable nix-ld

Adding the following to your nix configuration resolved the issue for me:

programs.nix-ld.enable = true;

Root Cause: Ideological Conflict in Filesystem Approaches

To fully understand this error, it’s essential to recognize the philosophical differences between most Linux distributions and NixOS regarding filesystem structuring.

The FHS World

Most Linux distros follow the Filesystem Hierarchy Standard (FHS). This standard defines a conventional directory layout: executables go in /bin, shared libraries in /usr/lib, etc. Binaries built externally assume that required shared libraries (e.g., .so files) and dynamic linkers (e.g., ld-linux-x86-64.so.2) will be available at these standard locations.

The NixOS World

NixOS intentionally disregards the FHS. All packages and dependencies are installed under unique, isolated paths in /nix/store containing hashes. Each binary is patched at build time to look for dependencies specifically in /nix/store. This system is the cornerstone of NixOS’s purity, reproducibility, and reliability.

The Point of Collision

The error arises from the collision of these two worlds. In this case, the MCP server that uvx tries to launch (internally Python-based) was built for an FHS-compliant system. When the Linux kernel attempts to execute such a binary, it looks for a hardcoded dynamic linker in the ELF header (e.g., /lib64/ld-linux-x86-64.so.2). Standard NixOS systems do not have a dynamic linker at that location, so the kernel fails to start the binary, giving the Could not start dynamically linked executable error.

Why Enabling nix-ld Works

When you enable nix-ld, a special program is installed at the FHS-standard dynamic linker location (e.g., /lib64/ld-linux-x86-64.so.2). When the kernel attempts to run a non-Nix FHS-compliant binary, it now starts the nix-ld shim program instead. This shim reads the NIX_LD_LIBRARY_PATH environment variable (which points to libraries in /nix/store, as specified in programs.nix-ld.libraries). With this info, the shim launches the “real” dynamic linker from /nix/store and passes along the correct library paths, allowing the binary to find its dependencies and start as if on a standard Linux environment.

Broader Context: Alternatives and Trade-offs

While powerful, nix-ld isn’t the only solution. Alternatives include buildFHSEnv (and wrappers like steam-run). Instead of providing FHS compatibility systemwide, buildFHSEnv constructs temporary, sandboxed FHS environments per application. This choice represents a classic engineering trade-off within the NixOS community:

  • nix-ld: Pragmatism. Increases overall compatibility and makes it easy to “just run” non-Nix binaries, but may slightly diminish NixOS’s purity.
  • buildFHSEnv: Purism. Maintains strict per-app isolation in line with NixOS philosophy, at the cost of more explicit, possibly tedious, configuration for each application.

Lesson Learned

This troubleshooting process holds lessons beyond mere bug fixing. It’s a microcosm of the broader challenges in modern software development: dependency management and achieving reproducible environments. How to manage the boundary—and resulting impedance mismatch—between a clean, declarative system (NixOS) and a messy, imperative world of pre-compiled, external software ecosystems. Tools like nix-ld serve as a kind of “Foreign Function Interface (FFI)” bridging different software philosophies, offering an elegant answer to this challenge. This experience is not just troubleshooting—it’s a valuable step toward understanding the nature of system integration.