Skip to content
← All notes

// notes

nix-direnv: "unshare failed: Operation not permitted"

direnv with use flake fails on unshare failed while dotenv works. I trace the cause (a nested user namespace) and show the fix for the nix wrapper

Published: 2026-10-084 min read
Nixdirenvnix-direnvuser-namespaceLinux

direnv exec . in a project with use flake fails with unshare: unshare failed: Operation not permitted, while variables from .env are picked up. unshare creates namespaces (isolated process environments), and the failure happens on the second one. Below: how to find the cause and fix the nix wrapper

Context: rootless Nix, direnv and nix-direnv in a project whose .envrc contains use flake

Symptom

Reproduces inside a project with an .envrc that contains use flake

direnv: loading $HOME/projects/demo/.envrc
direnv: using flake
direnv: nix-direnv: cache invalidated: profile (…/.direnv/flake-profile-…) does not exist
unshare: unshare failed: Operation not permitted
direnv: nix-direnv: Evaluating current devShell failed. Falling back to previous environment!

The dotenv part still works: simple variables (APP_ENV and the like) are set, and only the flake branch fails

Diagnosis

The first thing that confuses you: unshare failed does not say which process called unshare (the utility that creates a namespace). Searching the direnv configs and $HOME/.local/bin narrows it down quickly, but the exact answer comes from intercepting the binary: replace unshare with a logging script, put its directory first in PATH, and the caller shows up in the log. The technique is covered separately: How to find out who calls unshare

The interception showed that unshare was called by my own wrapper around nix. It raises a namespace and then, inside the one already raised, tries to create a second

Outside, the wrapper raises namespace #1. Inside it, nix-direnv calls nix, the wrapper raises a namespace again, and creating a nested namespace fails

The host where this reproduced blocks nested user namespaces as a matter of policy. In Linux such namespaces are generally allowed, so this is specific to that system

Cause

Let us define the terms. A user namespace isolates users and their privileges; a mount namespace isolates mount points. Rootless Nix uses them to mount /nix without root. A nested user namespace is created inside an existing one

The first unshare --mount --map-root-user succeeds: user and mount namespaces are created and the user is mapped to root. The second one, from inside namespace #1, is rejected by the kernel with EPERM. You can check the host policy like this:

sysctl user.max_user_namespaces
sysctl kernel.apparmor_restrict_unprivileged_userns
sysctl kernel.unprivileged_userns_clone

Values that forbid nested user namespaces: 0 for user.max_user_namespaces or kernel.unprivileged_userns_clone, and 1 for kernel.apparmor_restrict_unprivileged_userns. The set and presence of the keys depend on the distribution and kernel: kernel.unprivileged_userns_clone exists in Debian and derivatives, kernel.apparmor_restrict_unprivileged_userns only on systems with AppArmor, and user.max_user_namespaces is a general kernel counter. On the machine checked, for example, kernel.apparmor_restrict_unprivileged_userns was absent from /proc/sys and sysctl returned a cannot stat ... No such file or directory error

Any wrapper that raises a namespace unconditionally breaks inside one. That is the root cause

A similar but different problem: when the devShell PATH is silently not picked up in fish. Write-up: Nix devShell silently loses packages in fish

Fix

The wrapper around nix must be namespace-aware: if /nix/store is already mounted, call nix directly, without unshare

# $HOME/.local/bin/direnv-shims/nix
if [ -d /nix/store ]; then
    # We are already inside the namespace, so nix can be called directly
    for _d in "$HOME"/.nix/store/*-nix-[0-9]*; do
        case "$_d" in *.drv) continue;; esac
        [ -x "$_d/bin/nix" ] && exec "$_d/bin/nix" "$@"
    done
fi
# Outside, raise the namespace
exec "$HOME/.local/bin/nix-enter" nix "$@"

What happens here. If the store is mounted ([ -d /nix/store ]), the wrapper looks for an installed nix under $HOME/.nix/store and starts the first binary it finds via exec. Directories ending in *.drv are skipped: those are derivation descriptions, not installed packages. If the store is not mounted, the wrapper calls nix-enter and raises the namespace

The names nix-enter and direnv-shims/nix are examples; your wrapper may be named differently. What matters is the logic: check /nix/store before trying to create a namespace

Second step: run direnv itself entirely inside the namespace ($HOME/.local/bin/nix-enter-direnv). That is needed because nix-direnv uses hard-coded absolute paths under /nix/store/..., which do not exist outside. For the hard-coded paths and the bogus bash version error: nix-direnv: "minimum required bash version is 4.4"

Verify

There is only one reliable marker of "we are inside the namespace": a mounted store

test -d /nix/store && echo inside || echo outside

Do not rely on the $NIX_STORE_DIR variable: it can be set from outside, and then the check lies

Then rebuild the environment from scratch:

rm -rf .direnv && direnv allow

If the flake loads without unshare failed, the wrapper no longer creates a nested namespace

Pitfalls

  • The symptom blames the wrong culprit. unshare failed does not name the process; without intercepting the binary the search is blind
  • `/nix/store/...` paths in `PATH` silently do not work outside: command -v jq returns /usr/bin/jq. That is why it is worth keeping the whole interactive shell inside the namespace, not just direnv

Takeaway

unshare failed with direnv and nix-direnv means the nix wrapper is trying to create a second user namespace. Checking /nix/store before raising a namespace and running direnv inside it remove both the error and the bogus version diagnostics

If the error persists, check that the wrapper is not overridden elsewhere in PATH: for example, a second copy or a shell function with the same name in $HOME/.local/bin

Need this solved on your project?