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->rootpointer of the process. The previous root filesystem remains mounted in the background mount table, allowing processes with appropriate capabilities (likeCAP_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:
- 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.
- Subdirectory Destination: put_old (where the old root is placed) must be located under new_root.
- 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_PRIVATEorMS_SLAVE). - 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 executemount --make-rprivate /andmount --bind /opt/newroot /opt/newrootbeforehand.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 lacksCAP_SYS_ADMINcapabilities required to modify system mount namespaces.