Safely switch a Linux root filesystem with pivot_root
You will finish with a controlled sequence for making a mounted filesystem the new root, keeping the previous root at a known directory, and unmounting it when nothing still uses it. This is an early-boot and container-building operation, not a routine way to change a running server.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 20 minutes for a prepared test environment. You need root privileges, a mounted replacement filesystem containing the command and files you intend to run, and a mount namespace or maintenance environment that you can afford to disrupt. The installed package here is util-linux 2.39.3-9ubuntu6.6; the system manpage identifies util-linux 2.39.3. The shell's pivot_root currently resolves to a separate util-linux 2.41.3 installation, so check the path and version on your own host before relying on details.
1. Understand the operation before changing anything
pivot_root NEW_ROOT PUT_OLD moves the current root mount to PUT_OLD and makes NEW_ROOT the root mount. The second path must be inside the new root. After the switch, the old filesystem is still mounted until you unmount its new location.
This changes process filesystem context and can affect every process in the mount namespace. Do not experiment against the host's live root filesystem. Work in an initramfs, a container setup namespace, or a disposable virtual machine. A failed attempt can leave mounts and shells in an unexpected state; have console access and a recovery path.
Checkpoint: inspect the exact binary without changing state. On this machine the shell finds a separate Homebrew installation, while the packaged binary is under /usr/sbin:
$ command -v pivot_root
/home/linuxbrew/.linuxbrew/sbin/pivot_root
$ /usr/sbin/pivot_root --version
pivot_root from util-linux 2.39.3
Your output may show another path or version. Use the path you checked for the rest of the workflow. The command accepts only two positional paths, plus -h/--help and -V/--version.
2. Prepare a real mount point for the new root
Run the following as root in the maintenance environment. Replace the device and directory with values that you have already inspected. Mounting a device can expose or overwrite access to its contents, so stop if the device name is not unambiguous.
# mkdir -p /mnt/new-root
# mount /dev/NEW_ROOT_DEVICE /mnt/new-root
# mkdir -p /mnt/new-root/old-root
# findmnt /mnt/new-root
TARGET SOURCE FSTYPE OPTIONS
/mnt/new-root /dev/NEW_ROOT_DEVICE ext4 rw,...
NEW_ROOT must be a directory and must itself be a mount point. If you are using a directory rather than a separate filesystem, bind-mount it onto itself first:
# mount --bind /path/to/root-tree /path/to/root-tree
# findmnt /path/to/root-tree
The exact findmnt columns vary by util-linux version. The useful check is that the target appears as a mount point, rather than merely as an ordinary directory.
3. Check the mount-propagation boundary
Linux rejects a pivot when the new root, its parent, or the current root has shared propagation in the relevant place. This prevents the mount rearrangement from unexpectedly propagating to another namespace. Inspect the current root and the new root:
# findmnt -o TARGET,PROPAGATION /
# findmnt -o TARGET,PROPAGATION /mnt/new-root
If this is a private test namespace, make the relevant mounts private before continuing:
# mount --make-rprivate /
# findmnt -o TARGET,PROPAGATION /
This changes mount propagation for the namespace. It does not make the filesystem read-only and it does not undo other mount changes. Do not apply it blindly to a production host's shared mount arrangement; establish the namespace boundary first.
4. Perform the pivot
Change directory into the new root, then use relative paths. The relative form works whether the implementation has already adjusted the shell's root directory or current working directory:
# cd /mnt/new-root
# pivot_root . old-root
# printf 'pivot status: %s\n' "$?"
pivot status: 0
A zero status means the system call completed. It does not mean that the old root is ready to remove. The kernel requires the old root directory to be under the new root, and the new root to be a mount point. It also requires CAP_SYS_ADMIN in the user namespace that owns the mount namespace.
Common failures are useful diagnoses: EINVAL usually points to a non-mount-point new root, an old-root path outside it, or shared propagation; EPERM points to missing CAP_SYS_ADMIN; ENOTDIR means one of the paths is not a directory. Check those conditions before adding more privilege or retrying.
5. Re-enter the new root explicitly
The command's manpage accounts for implementations that may or may not change the calling shell's root and current directory. Use exec chroot . sh so the replacement shell is definitely rooted at the new filesystem:
# exec chroot . sh
# printf 'root: '; pwd
root: /
# test -x /bin/sh && printf '%s\n' 'new root is usable'
new root is usable
The chroot executable must be available under both the old and new roots, because the shell may still resolve it through either view. The new root also needs the shell and any libraries or devices that the command requires. If the shell cannot start, return to the maintenance environment and repair the root tree rather than guessing at paths.
The exec matters when you plan to unmount /old-root: it replaces the shell process instead of leaving the old executable running. Standard input, output and error can still refer to devices on the old filesystem, so attach them to the new root's console where the environment requires it, for example <dev/console >dev/console 2>&1.
6. Verify and remove the old root
From the new shell, confirm the old root is visible at the expected location and inspect the mounts before unmounting:
# findmnt /
# findmnt /old-root
# test -d /old-root && printf '%s\n' 'old root is reachable'
old root is reachable
Unmounting is disruptive and may be irreversible for unsaved work. First stop processes and services that still use the old root, and close shells whose current directory is there. Then unmount it:
# umount /old-root
# printf 'umount status: %s\n' "$?"
umount status: 0
If umount reports that the target is busy, do not jump to a lazy or forced unmount. Use findmnt and your process tools to find the remaining reference, then stop or move that process safely. A lazy unmount changes namespace visibility while references drain; a forced unmount can damage an active workload and is not a general recovery button.
There is no universal undo after the old root has been unmounted. Before that point, recovery is to leave the new shell with exit, return to the maintenance environment, and unmount or remount the new root as appropriate for that environment. For a persistent boot workflow, keep the previous initramfs or boot entry available and test rollback before deployment.
Done means
- The installed binary and util-linux version were checked.
- The replacement filesystem was mounted and verified as a mount point.
- Mount propagation was understood before the pivot.
pivot_root . old-rootreturned status 0 in a suitable namespace.- A new shell was started with the replacement root as
/. - The old root was unmounted only after references and recovery options were considered.