unroot lets your Linux system enter, modify, build within, and transport any
other Linux root filesystem. Different CPU architecture? No problem. Need to
become root without sudo? Done. Want to move an entire Linux system between
machines as a single archive? That too.
Think of it as a chroot, container daemon, cross-compiler, and system migration
tool had a baby — except it's just one small binary with no background services.
Working with Linux root filesystems usually means juggling multiple tools:
Traditional approach:
chroot setup → manual, error-prone
+ qemu-user-static → host-wide binfmt_misc configuration
+ tar → lose metadata without careful flags
+ sudo → host root access for everything
+ hand-crafted scripts → glue it all together
unroot approach:
unroot enter ~/rootfs → done
unroot brings together everything you need to work with Linux root filesystems: rootless multi-user chroots, automatic QEMU emulation, rootfs archive transport, and namespace-isolated root builds — all in one daemonless tool.
No container daemons. No hand-crafted chroot setups. No host-wide emulation configuration. Just one binary that does exactly what you need.
You've downloaded an ARM64 Raspberry Pi rootfs archive, but you're sitting at a much faster x86-64 Ryzen workstation. Instead of cross-compiling or booting the Pi, enter the rootfs directly:
unroot unpack raspi4-rootfs.tar.xz ~/roots/raspi4
unroot enter ~/roots/raspi4 -- /bin/shunpack preserves the rootfs's users, groups, permissions, capabilities, and
extended attributes using your subordinate UID and GID ranges. (These are extra
numeric identities allocated to your account in /etc/subuid and /etc/subgid —
unroot maps them to the rootfs's users and groups, so you can represent a
multi-user system without needing host root.) When /bin/sh turns out to be
ARM64, unroot automatically selects a static QEMU AArch64 emulator — or reuses
an existing QEMU installation if one is already registered — and registers it
in a private binfmt_misc namespace. (This means the emulator is active only
inside that rootfs, not system-wide. Your host's emulation configuration stays
unchanged.) From the shell, you can use the Raspberry Pi distribution's own
package manager, compiler, and tools as though you were running natively.
When you're done, capture the modified system:
unroot pack ~/roots/raspi4 raspi4-modified.tar.zstSay /mnt/raspi4-root is an NFS mount of a real Raspberry Pi root filesystem.
You want to compile a large project using the target system's own compiler and
libraries, but with the CPU and memory of your workstation:
sudo unroot enter --native /mnt/raspi4-root \
--env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
-- /bin/bashOnce inside, verify you're running in the ARM64 environment:
bash$ uname -a
Linux workstation 6.6.0 #1 SMP PREEMPT_DYNAMIC aarch64 GNU/Linux
bash$ cat /etc/os-release
NAME="Raspberry Pi OS"
VERSION="12 (bookworm)"You're now in a parallel ARM64 system on your x86-64 desktop. You can't see the Pi's running processes — this is the Pi's userspace, isolated from your host. But you can execute any command, use the Pi's package manager, and build software with the Pi's own toolchain, all while leveraging your workstation's 32 cores and memory. When you're ready to build:
bash$ cd /home/pi/src/large-project
bash$ make -j32Native mode keeps the mounted filesystem's existing ownership intact. unroot
detects the ARM64 executable, arranges the required host QEMU handler, and runs
the target's own toolchain through QEMU. Unlike rich roots, native mode operates
in the host's user namespace, so the binfmt_misc handler is registered
system-wide for the current boot. This isn't cross-compilation: it's the
Raspberry Pi userspace itself, executing on the larger machine. Build output is
written directly to the mounted rootfs.
Some embedded and system builds expect to run as root even when they don't need
root access to the host. single maps your ordinary UID and GID to 0 inside
a private user namespace:
mkdir -p "$HOME/roots/appliance"
unroot single \
--cwd "$PWD" \
--persist-env PATH \
--env DESTDIR="$HOME/roots/appliance" \
-- /bin/sh -c 'make -j32 && make install'Inside the command, id -u returns 0, root checks succeed, and you have
namespace-root capabilities. On the host, you're still your ordinary user —
mount and PID state stay private, and newly installed files remain manageable
by your regular account.
This mode needs an unprivileged user namespace but doesn't need subordinate UID or GID allocations. It's perfect for embedded systems where nearly every file is owned by root. If your build needs multiple users or service accounts, use a managed rich root instead.
You don't need a VM or container daemon just to run one program against a different userspace:
unroot enter ~/roots/debian-testing \
--map-ro "$PWD:/work" \
--env PATH=/bin:/usr/bin \
-- /usr/bin/python3 /work/check-release.pyThe process sees the selected rootfs as /, gets private mount and PID state,
exits normally, and leaves no daemon behind.
┌─────────────────────────────────────────────────────────────┐
│ Without unroot: │
│ │
│ chroot setup → manual prep, root required │
│ QEMU config → host-wide binfmt_misc, one-size-fits-all │
│ Archive extract → lose metadata without careful tar flags │
│ Multi-user → either root or broken ownership │
│ Cross-arch → manual emulator selection │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ With unroot: │
│ │
│ unroot enter ~/rootfs │
│ ↓ │
│ • Automatic QEMU for foreign architectures │
│ • Multi-user ownership without host root (rich mode) │
│ • Full metadata preservation (ACLs, xattrs, capabilities)│
│ • Private namespaces, no daemon │
│ • Explicit modes — no guessing │
└─────────────────────────────────────────────────────────────┘
Embedded developers — Build, modify, and test Raspberry Pi, embedded ARM, or RISC-V root filesystems on your x86-64 workstation without cross-compilation guesswork.
Distro builders — Create and maintain multi-user root filesystems as an ordinary user, preserving complete ownership and metadata without host root.
System migrators — Transport entire Linux systems between machines as single archives with full fidelity, including capabilities and ACLs.
Container skeptics — Get namespace isolation and rootfs flexibility without daemon overhead, image formats, or orchestration complexity.
Kernel developers — Run one process inside another userspace for testing, validation, or tooling without booting VMs or managing containers.
Linux root filesystems don't all have the same ownership requirements. unroot keeps those choices explicit rather than guessing or silently falling back:
-
Managed rich roots (default) — Created by
unroot unpack. Rootfs UID/GID 0 map to you, while subordinate ID ranges preserve multi-user ownership. (Subordinate IDs are extra numeric identities allocated to your account in/etc/subuidand/etc/subgid— unroot maps them to the rootfs's users and groups, letting you represent a complete multi-user system without host root.) The mapping is recorded in.unroot/meta.jsonand revalidated on every entry. Perfect for unprivileged multi-user build and packaging work. -
Native roots — Host-visible ownership unchanged. Use
sudo unroot unpack --nativeorsudo unroot enter --nativefor host-owned, NFS-mounted, or existing rootfs trees. Conventional privileged chroot behavior. -
Single mode — Runs a command as namespace root with the host filesystem visible:
unroot single -- COMMAND. Trusted builds needing namespace capabilities without subordinate IDs. Not a substitute for multi-user ownership.
A rich operation never degrades to single-ID ownership, and an unprivileged operation never silently becomes native host-root execution.
Enter a rootfs and start its default shell:
unroot enter ~/rootfsRun a specific command with a clean environment:
unroot enter ~/rootfs \
--cwd /build \
--env PATH=/bin:/usr/bin \
--env MAKEFLAGS=-j8 \
-- makeUse --persist-env to copy selected variables from the host. Bare command names
require an explicit PATH; absolute commands work without one.
Run a trusted build as namespace root:
unroot single --persist-env PATH -- /usr/bin/make -j8Foreign architecture execution is automatic — unroot detects the executable type and selects an appropriate emulator. If QEMU user-mode emulation is already installed and registered on your host, unroot will reuse it. For rich roots, you can also select an explicit static emulator and QEMU CPU:
unroot enter ~/arm64-rootfs \
--qemu /opt/qemu-aarch64-static \
--qemu-cpu cortex-a53 \
-- /bin/shUse --emulation never when a foreign target should fail rather than run under
QEMU. Native mode accepts a trusted root-owned emulator path but rejects
--qemu-cpu, because it operates in the host user namespace and its
binfmt_misc handler is registered system-wide, so it cannot carry per-rootfs
CPU policy.
These controls affect execution only. You remain responsible for selecting distro profiles, compiler flags, and CPU baselines.
unroot unpack ARCHIVE ROOT creates a managed rich rootfs and extracts the
archive inside its subordinate-ID mapping. It preflights helper binaries and ID
ranges before creating ROOT, avoiding partial extraction when the host isn't
configured for rich ownership.
Use sudo unroot unpack --native ARCHIVE ROOT for host numeric IDs. Both forms
record their ownership mode in ROOT/.unroot/meta.json.
unroot pack ROOT ARCHIVE validates the recorded mode before capture. It
preserves ownership, permissions, timestamps, links, sparse files, POSIX ACLs,
and extended attributes including file capabilities. SELinux labels are included
when active. Compression is selected from the destination suffix (.gz, .xz,
.zst).
Packing requires a managed rootfs. Unpacking requires an empty destination and
never overlays an existing tree. Host-specific .unroot metadata is excluded
from archives; incoming archives containing it are rejected.
unroot detects the ELF class, byte order, and machine type of executables, resolving scripts through their shebang. It doesn't assume every command is an ELF binary and rejects unsupported combinations rather than selecting an approximate emulator.
Rich roots use a private binfmt_misc instance and stage the static emulator
and unroot wrapper beneath .unroot/bin. Because rich roots run in a private
user namespace, the emulator is registered only inside that namespace — it
disappears with the process tree and never affects your host system.
Native roots use host binfmt_misc: unroot reuses an enabled handler with
matching ELF identity, or registers unroot-ARCH from a trusted static QEMU
executable. It never replaces a conflicting handler. Because native mode
operates in the host user namespace, the handler is registered system-wide and
remains active for the current boot.
There is no resident daemon or container image format. unroot provides the appropriate path for the environment in front of you.
unroot is designed for root filesystems and commands that you trust. It is not a general-purpose sandbox for hostile code.
Rich roots and single mode use a user namespace, so namespace root capabilities do not become host root privileges. Native mode deliberately uses host root. Every mode creates private mount and PID namespaces, keeping mount changes local to the process tree.
unroot shares the host network, IPC namespace, hostname, cgroup hierarchy, kernel, and hardware interfaces. It does not install syscall filters, impose resource limits, or create AppArmor or SELinux policies. Mapped paths and selected devices remain backed by host resources.
What you get:
- Transparency — Explicit modes, no guessing, no silent fallbacks
- Visibility — Metadata recorded in
.unroot/meta.json, revalidated on entry - Cooperation — Works with your existing workflows, not against them
- Simplicity — One binary, no daemon, no hand-crafted setup
The first public release focuses on dependable rootfs entry and transport: durable rich and native ownership, explicit single-mode execution, native and foreign-architecture commands, full-metadata tar import and export, minimal automatic mounts, environment and working-directory configuration, and exact child I/O and exit-status propagation.
Broader experiments such as fs.* mutation commands, OCI packaging, overlays,
and persistent sessions are deliberately outside the initial release. See
Initial Public Release Scope.
Tagged releases provide standalone static unroot binaries for x86-64 and
ARM64, plus target-native packages for Debian 13, Ubuntu 24.04 and 26.04,
Fedora 44, and Enterprise Linux 9. The native packages install both unroot
and the host-compatible unroot-util helper required by managed rich roots.
Where the target distribution provides one, packages recommend its static QEMU
user-mode provider. Normal apt and Fedora dnf installations therefore
include cross-architecture support automatically. EL9 does not package a QEMU
user-mode provider in its standard repositories, so users must supply one for
foreign execution. Users who only need single or same-architecture operation
may deliberately disable recommended or weak dependencies. Unroot does not
require a distribution-installed host-global binfmt_misc policy.
The standalone binary remains useful for single and same-architecture native
operation. Build from source or install a native package for the complete rich
rootfs feature set.
- Linux with the namespace operations required by the selected mode
- Unprivileged user namespaces for rich roots and
single unroot-util,/usr/bin/newuidmap,/usr/bin/newgidmap, and suitable host subordinate-ID allocations for rich roots (configured in/etc/subuidand/etc/subgidby your distribution)- Host root privileges for native roots
- Linux 5.12 or newer when using
--map-ro - Linux 6.7 or newer and a compatible static QEMU user emulator for private
rich-mode foreign execution (or an existing QEMU installation with
binfmt_mischandlers already registered) - Host
binfmt_miscand a trusted static QEMU emulator for native foreign execution when no compatible handler is already present - GNU tar for
packandunpack, plus the compression program selected by an archive suffix when creating compressed output - A C++17 compiler and GNU Make when building from source
make cli produces a statically linked bin/unroot namespace engine and a
dynamically linked bin/unroot-util host-integration helper. make install
places both in the same directory. When libsubid's development interface is
available, the helper uses the host's configured subordinate-ID provider;
otherwise it reads /etc/subuid and /etc/subgid. See
Host Integration Helper.
make cli
make checkmake test runs the fast C++ suite. make e2e exercises real namespaces using
a static BusyBox fixture. make check runs both. Release validation covers WSL,
Proxmox-hosted Linux, bare-metal Linux, containers, x86-64, ARM64, and both
primary cross-architecture directions; see Testing Unroot.
Unroot grew out of work on FFS, where it provides unprivileged rootfs entry and cross-architecture execution during Linux system builds. The public project is now being honed into a focused Kernel Seeds tool with its own release boundary.
Unroot is licensed under the GNU General Public License, version 3 only
(GPL-3.0-only). Vendored third-party components retain their original
licenses and copyright notices.