chroot & pivot_root

When a container sees / as its root, it's not seeing the host's real root. The container has a different root directory — a fake one. This trick is called chroot (change root). It's the oldest form of Linux isolation, dating back to 1979, and it's the foundation of container filesystem isolation today.

How chroot Works

What does chroot actually do? chroot() is a system call that changes a process's view of "/" to a different directory. After chroot("/mycontainer"), the process thinks "/mycontainer" is the root. It can't access anything above that directory — "/etc/passwd" now means "/mycontainer/etc/passwd". The kernel enforces this by resolving absolute paths relative to the new root.
# Build a minimal chroot environment mkdir -p /myroot/{bin,lib,lib64,etc,proc,sys,dev} # Copy bash and its dependencies cp /bin/bash /myroot/bin/ ldd /bin/bash # linux-vdso.so.1 (virtual) # libtinfo.so.6 => /lib/x86_64-linux-gnu/libtinfo.so.6 # libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 cp /lib/x86_64-linux-gnu/libtinfo.so.6 /myroot/lib/ cp /lib/x86_64-linux-gnu/libc.so.6 /myroot/lib/ cp /lib64/ld-linux-x86-64.so.2 /myroot/lib64/ # Enter the chroot chroot /myroot /bin/bash # Now: ls / # sees /myroot/ contents only pwd # / ls /etc # empty (we didn't copy anything) ls /proc # empty (not mounted)

Why chroot Alone Isn't Secure

If chroot limits what files a process can see, why isn't it secure? A root process inside a chroot can escape it. Using a combination of mkdir, chdir, and chroot() itself, a privileged process can navigate out of the chroot jail. Also: chroot doesn't isolate processes (you can still signal host processes), network (full access to host network), or devices (can read /dev/sda). It's filesystem-only isolation that breaks with root access.
# Classic chroot escape (requires root inside chroot): # mkdir temp # chroot temp /bin/bash ← nested chroot # cd .. ← navigate to real root! # Why containers use mount namespaces instead: # Mount namespace + pivot_root = proper isolation # The container process can't "reach up" past its mount namespace # Even root inside the container is contained # chroot is still used for: # - System rescue environments # - Build systems (debootstrap, Arch Linux install) # - Package building (pbuilder, mock) # But NOT for security isolation

pivot_root — Proper Filesystem Isolation

How do containers do filesystem isolation properly if chroot isn't secure? Containers use pivot_root inside a mount namespace. pivot_root() swaps the filesystem: the new root becomes the real root, the old root gets moved to a directory you specify (then unmounted). Combined with a mount namespace (so the mount changes don't affect the host), this provides true isolation — the container genuinely has no way to access host files.
# Container filesystem isolation process (simplified): # 1. Clone with CLONE_NEWNS — new mount namespace # 2. Mount container image as read-only lower layer # 3. Mount overlayfs on top (copy-on-write) # 4. pivot_root into the overlayfs mount # 5. Unmount the old root # 6. Container process starts with this as its / # Real container startup (runc does this): # unshare(CLONE_NEWNS) # mount("overlay", "/container/merged", "overlay", # "lowerdir=/images/base,upperdir=/containers/abc/diff, # workdir=/containers/abc/work") # pivot_root("/container/merged", "/container/merged/old-root") # umount("/old-root") ← host filesystem gone # execve("/entrypoint") # Alpine Linux image root (what container sees as /): ls /var/lib/docker/overlay2/abc.../merged/ # bin dev etc home lib media mnt proc root run srv sys tmp usr var

Building chroot Environments

# debootstrap — build a Debian base in a directory debootstrap --arch amd64 bookworm /mydebian http://deb.debian.org/debian/ chroot /mydebian bash # Arch Linux in a chroot (pacstrap): pacstrap -c /mychroot base arch-chroot /mychroot bash # sets up proc/sys/dev mounts automatically # Alpine Linux (lightweight — good for container base): # Download rootfs tarball, extract, chroot wget https://dl-cdn.alpinelinux.org/alpine/latest-stable/releases/x86_64/alpine-minirootfs-3.19.0-x86_64.tar.gz mkdir /myalpine tar xzf alpine-minirootfs-*.tar.gz -C /myalpine chroot /myalpine /bin/sh

Frequently Asked Questions

What will I learn here?

This page covers the core concepts and techniques you need to understand the topic and progress confidently to the next lesson.

How should I use this page?

Start with the overview, then follow the section links to deepen your understanding. Use the table of contents on the right to jump to specific sections.

What should I read next?

Use the navigation below to continue to the next lesson or explore related topics.