Nested chrooting in daemonless rootless Nix?

I’m using Silverblue with nix-coire. If i run a program from a flake (Say, Emacs), can’t I open another nix shell inside it?

This is what I get when trying to nix shell inside Emacs (which is installed from a flake):

antonio@X13:~/nix/devenv$ nix shell
error: setting up a private mount namespace: Operation not permitted

So it seems that a downside of rootless chroot is that they can’t be nested. Is there no workaround?

The error comes from Nix trying to set up its own private mount namespace, which isn’t permitted in your current environment.

One thing worth trying is:

nix shell --store local

You can also try setting it for just that command:

NIX_CONFIG='store = local' nix shell

This makes Nix use the local store directly, but I’m not sure it’ll fix the namespace error with nix-coire. I’d try it first rather than changing your global nix.conf, since that could affect your setup outside the environment too.

I couldn’t find documentation for nix-coire, so if this doesn’t work, its README or the project’s author might be able to clarify how its isolation works.

nix-coire

It actually is nix-core. Sorry for the typo. It’s in the Fedora repository (here) and some documentation is here.

Trying to nix shell --store local results in the following:

antonio@X13:~/nix/devenv$ nix shell --store local
error: creating directory "/nix/store": Read-only file system

This is a hard kernel restriction, not a configuration issue. Since Linux 3.9, the kernel returns EPERM when CLONE_NEWUSER is requested and the caller is in a chroot environment (i.e., the caller’s root directory doesn’t match the mount namespace root). So once you’re inside a chroot, you cannot create another user namespace — and therefore cannot do a second unshare -r.

I’ve had success using something like this:(Never on a Atomic system though)

use pivot_root instead of chroot.

pivot_root actually changes the mount namespace root, so the kernel’s “is the caller in a chroot?” check doesn’t trigger. You can then create nested user namespaces freely (the kernel allows up to 32 levels).

A minimal pattern:

# Outer level
unshare -rm bash -c '
  # pivot_root into the first rootfs
  mkdir /mnt/newroot /mnt/oldroot
  mount --bind /path/to/rootfs1 /mnt/newroot
  cd /mnt/newroot
  pivot_root . /mnt/oldroot
  umount -l /mnt/oldroot

  # Now you can create a SECOND user namespace inside
  unshare -rm bash -c "
    # pivot_root into the second rootfs
    mkdir /mnt/newroot /mnt/oldroot
    mount --bind /path/to/rootfs2 /mnt/newroot
    cd /mnt/newroot
    pivot_root . /mnt/oldroot
    umount -l /mnt/oldroot
    exec /bin/bash
  "
'

Practical caveats:

  • You need mount --bind to make the target a proper mount point before pivot_root.

  • You must clean up the old root (umount -l /mnt/oldroot) to avoid leaking.

  • If you’re on a hardened system, check user.max_user_namespaces (or kernel.unprivileged_userns_clone on Debian/Ubuntu) — if set to 0, even the first unshare -U will fail regardless of nesting.

Alternative approach: avoid nesting entirely. If the goal is to run Nix inside a sandbox, do all the mount setup (including the Nix store) in a single namespace rather than trying to layer chroots.

Reading my notes I did use some AI suggests.