How to Switch Root Filesystems Securely in Linux Using pivot_root

In low-level Linux systems administration, container runtime engineering, and initramfs bootloader design, securely changing the root filesystem of a running process tree is a fundamental requirement. While the traditional chroot command alters the apparent root directory for a process, it does not alter the underlying mount namespace. An unprivileged process or container root user can exploit open file descriptors, relative paths (..), or auxiliary system calls to break out of a traditional chroot environment. To achieve true root filesystem switching, the Linux kernel provides the pivot_root system call and command-line utility, which atomically swaps the current root mount with a new root filesystem.

Understanding How pivot_root Differs from chroot

The operational difference between chroot and pivot_root lies in how the Linux Virtual Filesystem (VFS) handles mount points and namespaces:

  • chroot: Modifies only the current->fs->root pointer of the process. The previous root filesystem remains mounted in the background mount table, allowing processes with appropriate capabilities (like CAP_SYS_CHROOT) to traverse out of the jail.
  • pivot_root: Operates at the mount namespace level. It makes the new directory the new root mount point (/) and moves the old root mount point to an assigned subfolder (such as /old-root). Once swapped, the administrator can completely unmount and detach the old root filesystem, making jailbreaks structurally impossible.

Modern container engines (such as Docker, containerd, and runc) and early user-space boot scripts (transitioning from temporary ramdisk initramfs to persistent NVMe/SSD root partitions) rely exclusively on pivot_root for secure root transitions.

Requirements and Constraints for pivot_root

The Linux kernel enforces strict architectural constraints before allowing a pivot_root operation to execute:

  1. Distinct Mount Points: new_root must be an active mount point (it cannot simply be a subdirectory of an existing mount). If it is not a mount point, you must bind-mount it to itself.
  2. Subdirectory Destination: put_old (where the old root is placed) must be located under new_root.
  3. Shared Mounts Prohibited: Neither new_root nor the current root filesystem can be marked as shared propagation (MS_SHARED). They must be marked as private (MS_PRIVATE or MS_SLAVE).
  4. Same Mount Namespace: Both locations must exist within the caller’s active mount namespace.

Step-by-Step Root Filesystem Pivoting Workflow

To safely test or execute a root pivot in Linux, establish an isolated mount namespace using unshare to avoid corrupting the host operating system’s global mount table.

Step 1: Enter an Isolated Mount Namespace

Launch a new bash shell inside a private mount and user namespace:

sudo unshare --mount --propagation private /bin/bash

Step 2: Prepare the New Root Filesystem

Assuming you have extracted a clean rootfs or container image into /opt/newroot, verify that the directory is an active mount point. Convert it into a mount point using a recursive bind mount:

mount --bind /opt/newroot /opt/newroot
mount --make-private /opt/newroot

Step 3: Create the put_old Placeholder Directory

Create a dedicated folder inside the new root filesystem where the old root filesystem will temporarily dock:

mkdir -p /opt/newroot/oldroot

Step 4: Execute the pivot_root Command

The syntax for the command is:

pivot_root new_root put_old

Execute the pivot:

cd /opt/newroot
pivot_root . oldroot

Immediately upon execution, the current working directory becomes the new system root (/). The previous host root filesystem is now accessible exclusively under /oldroot.

Mounting Essential Virtual Filesystems

Once inside the pivoted root, system utilities require the standard kernel pseudo-filesystems (/proc, /sys, and /dev) to operate correctly:

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev

Unmounting and Detaching the Old Root Filesystem

To finalize the security boundary and reclaim memory resources, unmount the old root filesystem that is now docked at /oldroot:

umount -l /oldroot
rmdir /oldroot

Using the lazy unmount switch (-l) cleanly detaches the old filesystem tree even if background daemons still hold lingering file descriptors. Once removed, the process environment is completely isolated: no path, pointer, or filesystem traversal exists back to the originating host storage layer.

Troubleshooting Common pivot_root Errors

If pivot_root fails with an error, check the following common causes:

  • Invalid argument (EINVAL): Indicates that new_root is not a mount point, or that the mount propagation is marked as shared. Ensure you execute mount --make-rprivate / and mount --bind /opt/newroot /opt/newroot beforehand.
  • Device or resource busy (EBUSY): Occurs if new_root or put_old overlaps with an existing root mount or if directories reside on conflicting NFS/CIFS network mounts.
  • Permission denied (EPERM): The calling process lacks CAP_SYS_ADMIN capabilities required to modify system mount namespaces.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.