chroot is a Unix and Linux system call, and the command-line tool built on it, that changes the apparent root directory of a process and all of its children, so that the path / resolves to a chosen subdirectory. In Linux security it is one of the oldest filesystem confinement techniques, used to limit which files a service can see and to build minimal environments for recovery, packaging and testing.
The mechanism is narrow by design: chroot restricts path lookup only. It does not isolate process IDs, network interfaces, users or kernel resources, and a process that keeps root privileges inside the new root can usually escape it. For that reason modern Linux treats chroot as a building block, not a security boundary, and combines it with namespaces, capabilities and mandatory access control.
How chroot changes path resolution
The kernel stores a root directory reference for every process alongside its current working directory. When a program opens an absolute path, the lookup starts from that root reference instead of the real filesystem root. The chroot(2) system call replaces the reference, and the change is inherited by every child process created afterwards with fork and exec.
Only a process holding the CAP_SYS_CHROOT capability may call chroot. The call also does not change the current working directory, which is the origin of the classic escape: a directory handle that still points outside the new root remains usable. Careful programs therefore call chdir("/") immediately after chroot and close every inherited file descriptor that references the outside tree.
The following table summarizes what the call changes and what it leaves untouched.
| Process attribute | Changed by chroot | Practical consequence |
|---|---|---|
| Root directory | Yes | Absolute paths resolve inside the new tree |
| Working directory | No | Must be reset with chdir to avoid escapes |
| Open file descriptors | No | Inherited handles still reach the host tree |
| User and group IDs | No | Root inside remains root on the host |
| PID, network, IPC views | No | Host processes and sockets stay visible |
| Mounted filesystems | No | Mounts must be prepared inside the tree |
Building a working chroot environment
A chroot directory must contain everything the confined program needs, because nothing outside it can be reached by path. Dynamically linked binaries require their shared libraries and the dynamic loader, name resolution needs files such as /etc/resolv.conf and /etc/nsswitch.conf, and many tools expect /dev/null, /dev/urandom and a mounted /proc.
Administrators usually populate the tree in one of three ways, each with different size and maintenance effort.
| Method | Typical size | Best suited for |
|---|---|---|
| Hand-copied binaries | A few megabytes | One static or small service |
| debootstrap or dnf –installroot | About 150 to 400 MB | Full distribution userland for builds |
| Statically linked program | Under 10 MB | Minimal jail with no library copies |
| Bind-mounted host directories | No extra space | Recovery shells and package builds |
For a rescue chroot, the typical sequence is to mount the damaged root partition, bind-mount /dev, /proc, /sys and /run into it, and run chroot /mnt /bin/bash. Tools such as arch-chroot automate these bind mounts. Package builders like pbuilder and mock use the same idea to compile software against a clean, reproducible userland.
Why chroot is not a security boundary
chroot was never designed to contain a hostile privileged process. A process with root inside the jail can create a new directory, chroot into it while keeping a handle on the old root, and walk upward with repeated .. lookups until it reaches the real root. It can also create device nodes with mknod, mount filesystems, load kernel modules or attach to host processes with ptrace.
Even an unprivileged process inside a chroot shares the host PID namespace and network stack, so it can see other processes, open sockets and reach local services. The table lists the main escape and leakage routes together with the control that closes each one.
| Escape or leak route | Requires | Mitigation |
|---|---|---|
| Nested chroot with open handle | CAP_SYS_CHROOT | Drop root and capabilities after chroot |
| Device node creation | CAP_MKNOD | Mount the tree with nodev |
| Mounting filesystems | CAP_SYS_ADMIN | Use a mount namespace and drop capabilities |
| ptrace on host processes | Same UID or CAP_SYS_PTRACE | PID namespace, Yama ptrace scope |
| Leaked file descriptors | Careless parent process | Close descriptors before exec |
| Setuid binaries inside the tree | Writable or copied binaries | Mount with nosuid, remove setuid bits |
The decisive rule is simple: drop privileges after entering the jail. A daemon that calls chroot, then changes to an unprivileged UID and discards its capabilities, gains real protection against path-based attacks, while a root shell in a chroot gains almost none.
chroot compared with other Linux isolation tools
Modern isolation on Linux builds on namespaces and seccomp, with chroot or its stronger relative pivot_root handling only the filesystem view. Neighboring Terms in Linux security such as AppArmor, SELinux, Seccomp and Linux capabilities each confine a different dimension, and they work best in combination.
| Mechanism | What it restricts | Strength as a boundary |
|---|---|---|
| chroot | Filesystem path lookup | Weak; escapable with root |
| pivot_root in a mount namespace | Entire mount tree | Strong when the old root is unmounted |
| Namespaces | PIDs, network, users, IPC, mounts | Strong, basis of containers |
| Seccomp | Allowed system calls | Strong reduction of kernel attack surface |
| AppArmor or SELinux | File, network and capability access by policy | Strong, enforced by the kernel |
| Linux capabilities | Individual root privileges | Strong when unneeded ones are dropped |
Container runtimes use pivot_root rather than chroot because it detaches the old root completely, which removes the nested-chroot escape. systemd offers RootDirectory= for chroot-style confinement and RootImage= together with ProtectSystem and PrivateTmp for a more complete sandbox.
Where chroot is still the right choice
chroot remains useful wherever the goal is a different filesystem view rather than strong isolation. Typical cases include repairing a system that no longer boots, installing a bootloader from live media, building packages, running a legacy userland on a newer kernel and restricting SFTP users to their home directories with the OpenSSH ChrootDirectory option.
- System recovery: reinstalling GRUB or regenerating an initramfs from a live USB.
- Privilege-separated daemons: OpenSSH, Postfix, BIND and NTP servers can chroot their unprivileged worker processes.
- SFTP jails: the jail directory must be owned by root and not writable by the user, or sshd refuses the login.
- Cross-architecture builds: combined with QEMU user-mode emulation for ARM or RISC-V userlands.
A common mistake is copying a setuid binary such as su or sudo into the jail, which hands an attacker a ready escalation path. Another is leaving the jail directory writable by the confined user, which allows planting hard links or crafted configuration files.
The short version
Imagine handing someone a building plan where one room is labeled as the whole building. Wherever they look on that plan, they only see the walls of that one room. chroot does the same for a program: it tells the program that a single folder is the top of the entire disk, so files outside that folder simply do not appear.
This is handy for repairs and for keeping a service away from files it has no business reading. It is not a locked vault, however. A program with administrator power can find its way out, so serious isolation also needs the other tools Linux provides.
Where it makes a difference
A hosting provider lets about 200 customers upload website files over SFTP. Without confinement, each customer could browse system files, including /etc, other customers’ home directories and log files on the shared server. The administrator adds a Match Group sftponly block to the sshd configuration with ChrootDirectory /srv/sftp/%u and ForceCommand internal-sftp.
Each jail directory is owned by root with mode 755, and a writable upload subdirectory belongs to the customer. Because internal-sftp runs inside the sshd process, no shell, libraries or device files need to be copied into the jail. After the change, a customer who logs in sees only their own tree, and an audit confirms that cross-customer file access is no longer possible through SFTP, while shell access remains disabled for the group.