Things on this page are fragmentary and immature notes/thoughts of the author. Please read with your own judgement!
The following content is edited based the answer generated by Google AI Studio using the prompt "buildFHSUserEnv vs mkShell".
mkShell (and nix-shell, nix develop)
Purpose: The standard way to create a development environment. Its primary goal is to make tools (compilers, interpreters, libraries, utilities) available in your shell session, primarily by manipulating environment variables.
Mechanism:
Modifies environment variables like PATH (to find executables), LD_LIBRARY_PATH (for dynamic linking, although Nix often uses wrappers/patchelf to avoid needing this directly), PKG_CONFIG_PATH, language-specific paths (PYTHONPATH, NODE_PATH), etc.
Makes specified Nix packages (buildInputs, nativeBuildInputs, packages) available in these paths.
Can run arbitrary shell code via shellHook when entering the environment (e.g., set aliases, source virtualenvs, start services).
Environment Structure: The underlying filesystem remains the standard Nix store layout (/nix/store/...). Tools are typically symlinked into a temporary profile directory which is then added to the PATH. It does not try to simulate a traditional FHS (Filesystem Hierarchy Standard) layout like /usr/bin, /lib, etc.
Use Case: The vast majority of development tasks. Compiling code (C, C++, Rust, Go), running scripts (Python, Node.js, Ruby, Shell), using specific command-line tools, etc., where the tools respect standard environment variables for locating dependencies. This is the idiomatic Nix way for development.
Compatibility: Works excellently for software designed to be built/run in diverse environments or that relies on standard environment variable lookups. It may fail for software that hardcodes paths assuming an FHS layout (e.g., looking for /usr/lib/libfoo.so instead of using the linker path).
How to Use:
Flakes: Define devShells in your flake.nix using pkgs.mkShell. Enter with nix develop.
Legacy Nix: Use nix-shell with a shell.nix file (often implicitly calls mkShell or uses pkgs.stdenv.mkDerivation which mkShell wraps) or directly on the command line (nix-shell -p pkg1 pkg2).
buildFHSUserEnv
Purpose: To create a sandboxed environment that simulates a traditional FHS layout (like /usr, /lib, /bin). This is primarily for running software (often pre-compiled binaries) that expects this specific filesystem structure and doesn't work correctly in a standard Nix environment.
Mechanism:
Uses Linux user namespaces (unshare) and bind mounts.
It constructs a new filesystem view inside the sandbox.
It takes specified Nix packages (targetPkgs) and symlinks or bind-mounts their contents into standard FHS locations within the sandbox (e.g., binaries into /usr/bin, libraries into /usr/lib).
The host filesystem is typically mounted under /host-fs or similar inside the sandbox.
Environment Structure: Inside the environment created by buildFHSUserEnv, you see directories like /usr/bin, /lib, /etc, populated with the requested packages. This looks much like a traditional Linux distribution's filesystem, even though the underlying files still reside in /nix/store.
Use Case:
Running pre-compiled, dynamically linked binaries that hardcode FHS paths (e.g., proprietary software, some games distributed as .deb or .tar.gz, AppImages that aren't fully self-contained).
Running build systems that make strong assumptions about FHS.
Testing packaging for traditional distributions.
Running Steam and many Steam games.
Compatibility: Provides high compatibility for software specifically expecting an FHS layout.
How to Use:
Typically defined as a Nix derivation itself. You build it like any other package, and the result is often a script that, when run, drops you into the FHS sandbox.
Can be called within a shell.nix or flake.nix to provide an FHS environment as part of a development shell, but it's a distinct layer.
mkShell vs buildFHSUserEnv Comparison
Feature | mkShell |
buildFHSUserEnv |
---|---|---|
Primary Goal | Development Environment | FHS Compatibility / Sandbox |
Mechanism | Environment Variables (PATH , etc.), Hooks |
User Namespaces, Bind Mounts, Symlinks |
Filesystem View | Standard Nix (/nix/store/... ) |
Simulated FHS (/usr , /lib , etc.) within sandbox |
Standard Usage | nix develop , nix-shell |
Run script produced by building the derivation |
Core Use Case | Building/running Nix-aware or portable software | Running software hardcoding FHS paths (binaries, games) |
Complexity | Simpler, more idiomatic Nix | More complex setup, requires kernel support (namespaces) |
Overhead | Lower (mostly env var setup) | Higher (namespace/mount creation) |
Analogy:
mkShell: Like giving a chef specific, high-quality ingredients and tools laid out on their standard workbench ($PATH, etc.). They know where to find everything based on the labels (env vars).
buildFHSUserEnv: Like building a replica of a specific historical kitchen inside a modern studio (the sandbox), placing ingredients and tools exactly where the historical chef expects them (/usr/bin, /usr/lib). The chef only sees the replica kitchen.
When to Choose Which:
Start with mkShell: For almost all development tasks, mkShell (via nix develop or nix-shell) is the correct, standard, and most efficient choice.
Use buildFHSUserEnv when mkShell isn't enough: If you have a specific program (often a pre-compiled binary) that fails to run because it cannot find its dependencies even when they are provided via mkShell, and error messages indicate it's looking in hardcoded paths like /lib or /usr/bin, then buildFHSUserEnv is the tool to reach for.