When you start a container with Podman or Docker, it feels like you’re starting a completely separate operating system.
The container has its own /etc directory. It has its own /usr directory. It has its own /var directory. You can install packages inside the container, edit configuration files, or even remove system binaries without affecting your host machine.
At first glance, this seems impossible.
Linux only has one filesystem. Your computer only has one root directory (/). So how can a process suddenly see a completely different filesystem?
Understanding the answer to that question is one of the first steps toward understanding how containers work.
Long before Docker, Podman, Kubernetes, or cloud-native applications became popular, Unix systems already had a feature that could change how a process viewed the filesystem. That feature is called chroot, short for change root.
Although modern container runtimes rely on several Linux kernel features, chroot introduced one of the most important ideas behind containers: filesystem isolation.
In this guide, you’ll build a minimal Ubuntu root filesystem, enter it using chroot, explore what changes, and understand why this feature still matters today.
Rather than making changes directly on your workstation, you’ll run every experiment inside a disposable Ubuntu container using Podman. This gives you a safe environment to learn without worrying about breaking your host operating system.
Before we begin
This guide assumes you’re comfortable using the Linux terminal and have Podman installed on your machine.
You do not need to use Ubuntu as your desktop operating system.
Whether you’re running Fedora, Debian, Arch Linux, openSUSE, or another Linux distribution, you’ll perform all of the work inside an Ubuntu container.
This keeps the environment consistent for every reader and makes it easy to delete everything when you’re finished.
The examples in this article were tested on:
Fedora Workstation (host)
Podman (or Docker)
Ubuntu 24.04 LTS (container)
The same concepts apply to Docker if that’s your preferred container runtime.
Why are we using a container?
This might seem like an unusual decision. After all, we’re trying to learn about chroot. Why introduce containers before we’ve even discussed what chroot does?
The answer comes down to safety and consistency. One of the goals of this article is to help you experiment freely. You’ll:
Create directories that look like complete Linux installations,
Download system packages, and
Change the apparent root directory of a running shell.
Although these actions are generally safe, it’s good engineering practice to perform experiments in an isolated environment whenever possible.
Also, running everything inside a disposable container gives us several advantages.
Your host operating system remains untouched.
Every reader starts with the same environment.
If something goes wrong, you can simply remove the container and create another one.
The commands behave the same regardless of the Linux distribution running on your laptop.

Figure 1: Pictorial representation of the order
Notice that we’re already working inside one isolated environment before creating another?
This layered approach mirrors how engineers safely test operating systems, package managers, and low-level Linux features in production environments.
The problem chroot was designed to solve
To appreciate chroot, it’s worth asking a simple question.
Imagine you’re creating an Ubuntu installation ISO. Before users can install Ubuntu on their computers, someone has to build the filesystem that eventually appears on the installation media.
Where does that filesystem come from?
Or perhaps you’re recovering a broken Linux installation and your operating system no longer boots, but your files are still intact. How do you repair the system if you can’t boot into it?
These situations seem unrelated, but they all require the same capability. They need a process to behave as though another directory were the root of the operating system, and that’s exactly the problem chroot solves.
It allows a process to treat an ordinary directory as though it were /. Now, you might have noticed something important. We haven’t talked about security, containers, or isolation.
Originally, chroot was created because operating system developers needed a practical way to work inside another root filesystem. Containers came much later.
Understanding this history makes it much easier to understand why chroot behaves the way it does today.
Every Linux process starts from the same place
Here’s how Linux normally finds files. Let’s say you run the following command cat /etc/os-release, this path begins with a forward slash (/), representing the root directory.
Whenever Linux resolves an absolute path, it starts from the root directory and walks one component at a time.
In this example, Linux performs something conceptually similar to this:
/
└── etc
└── os-releaseEvery absolute path follows the same .
/bin/bash
/usr/bin/python3
/var/log/messagesprocess
/home/gideon/.bashrcThey all begin at the exact same place, the root directory.
Process
│
▼
/
│
├── etc
├── usr
├── home
├── var
└── binNow imagine… what if, for one process only, Linux started here instead?
/home/gideon/minThe process would still ask for:
/etc/passwdBut Linux would quietly interpret that request as:
/home/gideon/min/etc/passwdThe application wouldn’t know anything had changed; it would genuinely believe that /home/gideon/min was the root of the operating system. That single idea is the foundation of chroot.
It doesn’t create another filesystem, kernel, or operating system. It simply changes where Linux begins resolving absolute paths for a particular process. And yet, it became one of the foundational building blocks that modern container runtimes would later expand upon.
Before we build our own Ubuntu root filesystem from scratch, we first need to answer another important question:
What exactly is a Linux root filesystem, and how do you create one without installing an operating system?
What is a root filesystem?
Many people think of Ubuntu, Fedora, Debian, or Arch Linux as operating systems. That’s true, but it’s only part of the picture.
A Linux system is made up of two major components:
The Linux kernel
User space
The kernel is responsible for talking to the hardware; it manages memory, schedules processes, communicates with storage devices, manages networking, and exposes interfaces that applications use through system calls.
Everything else lives in user space.
User space includes /bin, /usr, /etc, /var, /home, and /lib along with thousands of programs such as Bash, systemd, apt, Python, OpenSSH, and coreutils.
Collectively, these directories form what is known as the root filesystem. Think of the kernel as the engine of a car. The root filesystem contains almost everything else. Without the engine, the car cannot move. Without the body, wheels, steering wheel, and dashboard, the engine is not very useful either.
Linux works the same way; the kernel and the root filesystem complement one another.

Figure 2: Linux OS Architecture: Applications interact with the kernel via system calls, which manages hardware through device drivers
This distinction explains something interesting about containers: they don’t ship with their own kernel.
Instead, every container on your machine shares the kernel provided by the host operating system.
What changes from one container to another is the root filesystem, that is why an Ubuntu container can run on a Fedora host.
The Ubuntu container brings its own user space with Fedora providing the kernel, which is one of the reasons containers start much faster than virtual machines — a virtual machine boots an entirely new kernel, while a container simply starts another process.
Building a root filesystem without installing Ubuntu
At this point, you might be wondering, “If the root filesystem contains all of Ubuntu’s programs and configuration files…how do we create one?”
One option would be installing Ubuntu into a virtual machine.
Another option would be installing Ubuntu onto another partition. Both approaches work, but also much heavier than necessary.
This is where debootstrap comes in.
What is debootstrap?
debootstrap is a tool maintained by the Debian project. It creates a Debian or Ubuntu root filesystem inside an ordinary directory.
What it doesn’t do:
install a bootloader
partition disks
format storage devices
install a kernel
reboot your computer
Instead, it downloads the packages required for a minimal Ubuntu installation and extracts them into the directory you specify. When it finishes, you have a complete Ubuntu userspace ready to be used.
You will find similar ideas in Ubuntu’s installer, many container images, recovery environments, and build systems. Even cloud images often begin life as a root filesystem created by tools like debootstrap.
In other words, it’s part of how Linux distributions build themselves.

Figure 3: How debootstrap Builds a Minimal Debian Root Filesystem
Looking at the command
Install debootstrap inside your Ubuntu container with the following commands.
apt update
apt install -y debootstrapNow, create a directory that will hold our new filesystem. Let’s call it min.
mkdir minFinally, run:
debootstrap noble min2 http://archive.ubuntu.com/ubuntu/Let’s break this command down…
debootstrap runs the program while “noble” specifies the Ubuntu release. In this case, we’re creating an Ubuntu 24.04 (Noble Numbat) filesystem.
min is the directory where the new filesystem will be created. It can be almost any empty directory.
http://archive.ubuntu.com/ubuntu/ is the package repository that debootstrap downloads packages from.
What happens when debootstrap runs?
Let’s walk through the process. First, debootstrap downloads the metadata describing Ubuntu’s packages. From that metadata, it determines which packages are considered essential for a working Ubuntu userspace.
That includes packages such as:
base-files
bash
coreutils
libc6
dpkg
apt
Each package is downloaded as a .deb archive. If you’ve ever installed software manually using dpkg, you’ve already worked with these packages.
What many people don’t realize is that a .deb file is simply an archive containing files and metadata.

Figure 4: The anatomy of a typical .deb Package: What’s Inside bash.deb
debootstrap extracts these archives one by one into the target directory. As more packages are extracted, the directory gradually transforms into a complete Ubuntu filesystem.
You’ll see output similar to this:
I: Retrieving base-files...
I: Retrieving bash...
I: Retrieving libc6...
I: Extracting base-files...
I: Extracting bash...
...Every “Retrieving” message downloads another package, while every “Extracting” message copies files into your new root filesystem. Eventually, you’ll see a success message indicating that the base system has been installed.
At that point, your min directory becomes an Ubuntu userspace waiting for a process to use it as its root.
Exploring the new filesystem
Before we rush into chroot, let’s look at what debootstrap actually created.
Run tree -L 1 min, or if tree isn’t installed find min -maxdepth 1.
You should see something similar to this:
min/
├── bin
├── boot
├── dev
├── etc
├── home
├── lib
├── media
├── proc
├── root
├── run
├── sbin
├── sys
├── tmp
├── usr
└── varThis directory layout is what Linux applications expect. Nothing here is running; there are no processes, services, kernel, or scheduler. It’s simply a filesystem.
This is one of the most important ideas in this article. A root filesystem is simply the collection of files that an operating system expects to find once the kernel begins executing programs.
That’s why we can build one without booting anything.

Figure 5: Screenshot showing the output of tree -L 1 min
A filesystem waiting for a process
At this point, we have something interesting, a complete Ubuntu filesystem sitting inside an ordinary directory. But Linux doesn’t automatically use it.
If you run ls /, you’re still looking at the root of the Ubuntu container. So the next question is:
“How do we convince a single process that this new directory is actually /?”
This is where chroot finally enters the picture. Run the command chroot min. If everything has been created correctly, your prompt changes.
#
At first glance, this doesn’t seem very exciting. In reality, something very significant just occurred: Linux changed the root directory for this shell process. Not for every process.
This distinction is one of the most important ideas in this article.

Figure 6: Screenshot showing successfully entery ofthe new root filesystem with chroot
What changed?
To answer that question, let's compare the environment before and after running chroot.
Before entering the new root filesystem, run:
pwd
ls /You should see something similar to:
/
bin
boot
dev
etc
home
min2
proc
sys
usr
varNotice that the min directory exists. That’s expected because you’re still looking at the container’s root filesystem.
Now, enter the new root using chroot min. Run the same commands again.
pwd
ls /This time you'll see something like:
/
bin
boot
dev
etc
home
proc
root
run
sys
tmp
usr
varmin became /. Remember how Linux resolves absolute paths? Before chroot, Linux interpreted /etc/passwd like this:
Container Root
│
▼
/
└── etc
└── passwdAfter chroot, Linux interprets exactly the same path differently.
min2
│
▼
/
└── etc
└── passwdThe application never notices the difference; it asks for /etc/passwd and the kernel quietly redirects that request into the new root filesystem.
It simply changes where pathname resolution begins — same kernel, different root filesystem.
Can the process escape?
If / now points to min, surely the shell can't access files outside it. Try this:
cd ..
pwdYou’d still be at / no matter how many times you execute cd ... From the shell’s perspective, the top of the filesystem has already been reached.
This behaviour often leads people to believe that chroot is a security feature. It isn’t, and we’ll see why later.
For now, remember this: chroot changes path resolution, not permissions.
Why some commands don't work
Let’s try another experiment. Run ps.
Depending on your environment, you may receive an error.
Now try mount, or perhaps ip addr.
Some commands work while others don’t. Why?
The answer teaches us another important lesson about Linux. Not everything you see under / actually exists on disk.
Earlier, debootstrap created directories such as:
/proc
/sys
/devBut those directories are empty because the real contents of those directories are created dynamically by the kernel. They’re virtual filesystems, and the kernel generates them on demand.
Think about /proc.
When you execute cat /proc/cpuinfo there isn’t a file named cpuinfo sitting on your SSD. The kernel creates its contents when the file is read. The same is true for:
/proc/meminfo
/proc/self
/proc/uptimeThey are not ordinary files; they’re interfaces into the running kernel.

Figure 7: How the kernel generates virtual files on demand
This is why debootstrap couldn’t possibly populate /proc. Those files don’t belong to Ubuntu, they belong to the running Linux kernel.
Mounting the missing pieces
To make the new environment behave more like a complete Linux system, we need to expose the kernel’s virtual filesystems inside the new root.
Exit the chroot.
exitNow, let’s bind-mount one of the required directories.
mount --bind /proc min/procThe command creates another view of an existing filesystem.
Notice that we didn’t copy or duplicate anything. We simply made the same kernel-backed filesystem visible from another location.
This idea — presenting the same underlying resource at multiple points in the filesystem — is fundamental to Linux.
It’s also something container runtimes rely on extensively.

Figure 8: Screenshot showing bind mounting /proc
Enter the chroot again.
chroot minNow these commands try:
cat /proc/cpuinfo
mount
psYou’ll notice that many more commands now behave as expected because the process can finally communicate with the kernel through the standard virtual filesystems.
At this point, our environment feels much more like a complete Linux system.
But there is still one important question left unanswered.
If chroot can give a process a completely different root filesystem, why did Linux eventually need containers?
Why chroot is not a container
Seeing that chroot can make a process believe that another directory is the root of the filesystem sounds very much like what containers do.
After all, when you start an Ubuntu container on a Fedora machine, the container also believes that its root directory is /.
The difference is that beyond the resolution of absolute paths, everything else remains shared.
To understand this, let’s look at what a process sees after entering a chroot.
It has:
a different root filesystem
the same kernel
the same process table
the same network stack
the same hostname
the same user IDs
the same system clock
the same memory management
Only one of those changed.

Figure 9: chroot isolation at work
What chroot does not isolate
Suppose you have two terminals.
Terminal A:
sleep 1000Terminal B:
chroot min2Now run ps aux you’ll probably notice something surprising. You can still see the sleep process because chroot doesn’t isolate processes. There is still only one process table managed by the kernel.
Likewise, if you run ip addr you’ll still see the same network interfaces; the shell inside the chroot is still using the host’s networking stack. And I think we already know that hostname returns exactly the same hostname.
The same applies to:
shared memory
IPC
mounted filesystems
system time
chroot was never designed to isolate these resources.
How containers extend the idea
Modern containers start with the same idea as chroot — every container needs its own root filesystem. But container runtimes don’t stop there. Instead, they isolate almost every major resource in Linux.
For example:

Figure 10: Table showing the difference in isolation in chroot vs in containers
Containers achieve this by combining several Linux kernel features.
The most important ones are:
Mount namespaces
PID namespaces
Network namespaces
User namespaces
UTS namespaces
IPC namespaces
cgroups
Each feature isolates one part of the operating system. Together, they create what feels like a completely separate machine.
I am not trying to paint chroot as obsolete. All I am trying to show is that it simply became one piece of a much larger system.
Following a file lookup
Let's revisit the idea that started this article.
Suppose Bash needs to open:
/etc/passwdWithout chroot, the kernel resolves the path like this.
/
↓
etc
↓
passwdAfter chroot, the kernel starts somewhere else.
min
↓
etc
↓
passwdModern containers perform a similar operation. The difference is that the process is also placed inside its own mount namespace, having its own view of mounted filesystems.
So instead of merely changing the starting directory, the process receives an entirely different filesystem hierarchy.
That’s one reason containers are much harder to escape.
Where chroot is still used today
Although containers have become the standard way to isolate applications, chroot is still widely used.
For example, Linux administrators commonly use it when repairing systems that no longer boot. This technique has been used for decades and remains one of the most effective ways to recover Linux systems.
Another common use is package building.
Distribution maintainers frequently build software inside isolated root filesystems to ensure that packages don’t accidentally depend on software installed on the developer’s workstation.
Many continuous integration systems use the same idea.
Key takeaways
Before moving on, make sure these ideas are clear.
chrootstands for change root.It changes where the kernel begins resolving absolute paths.
A root filesystem is simply a collection of files expected by Linux user space.
debootstrapcreates a Debian or Ubuntu root filesystem without installing an operating system.chrootdoes not start a new kernel.Every process inside the
chrootstill shares the host kernel.Virtual filesystems such as
/proc,/sys, and/devare provided by the running kernel and usually need to be mounted into the new root.Containers build upon the same concept but add isolation for processes, networking, users, hostnames, IPC, and resources.
If you remember only one sentence from this article, remember this:
chroot doesn’t create another operating system. It changes a process’s view of where the operating system begins.
Once that idea clicks, many of the design decisions behind modern Linux containers become much easier to understand.

