When engineers use Docker, Podman, or Kubernetes, they are interacting with high-level abstractions. Beneath the surface, “containers” do not actually exist as a first-class construct in the Linux kernel. A container is simply a standard Linux process that has been heavily restricted using three native kernel features: Namespaces (isolation), Control Groups (resource limits), and a Union File System (layering).
To truly understand how modern container orchestration functions, system administrators should know how to build a container entirely from scratch using raw Linux commands. This guide explains how to construct a fully isolated, ephemeral container runtime manually, utilizing unshare for Namespaces and mount for OverlayFS.
Understanding the Architecture of a Container
If you run a web server binary on Linux, it can see the entire filesystem, every process running on the host, and all network interfaces. To “containerize” it, we must lie to it.
- Namespaces (The Lie): We use the
unsharesystem call to create new, blank namespaces. We give the process its own PID namespace (so it thinks it is PID 1), its own Network namespace (so it only seeseth0of the container, not the host), and its own Mount namespace. - OverlayFS (The Filesystem): A container needs a Root Filesystem (rootfs) like Alpine or Ubuntu to execute binaries like
/bin/sh. However, containers must be ephemeral. We use OverlayFS to stack a read-write “Upper” layer on top of a read-only “Lower” layer (the Ubuntu image). When the container writes a file, it goes to the Upper layer, leaving the base image untouched. - chroot (The Jail): Once the namespaces and filesystem are ready, we use
chrootto pivot the root directory of the process into the OverlayFS mount, trapping it.
Step 1: Preparing the Root Filesystem (rootfs)
First, we need a base image. We will download a minimal Alpine Linux root filesystem.
mkdir -p /tmp/mycontainer/base
cd /tmp/mycontainer
wget https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/x86_64/alpine-minirootfs-3.18.4-x86_64.tar.gz
tar -xzf alpine-minirootfs-3.18.4-x86_64.tar.gz -C base/
The /tmp/mycontainer/base directory now contains a full Linux filesystem (/bin, /etc, /usr).
Step 2: Constructing the Ephemeral OverlayFS
We want our container to be able to write files (e.g., log files in /var/log) without permanently modifying the base directory. We will use OverlayFS.
Create the necessary directories for the overlay:
mkdir -p /tmp/mycontainer/upper
mkdir -p /tmp/mycontainer/work
mkdir -p /tmp/mycontainer/merged
Now, mount the OverlayFS. We define base as the lower (read-only) directory, upper as the writable layer, and merged as the final unified view that the container will see.
sudo mount -t overlay overlay -o lowerdir=/tmp/mycontainer/base,upperdir=/tmp/mycontainer/upper,workdir=/tmp/mycontainer/work /tmp/mycontainer/merged
If you ls /tmp/mycontainer/merged, you will see the Alpine filesystem. If you create a file inside merged, the actual bits are written to the upper directory on the host.
Step 3: Creating the Namespaces and Jailing the Process
We will use the unshare command to launch a new process, explicitly detaching it from the host’s namespaces. We will isolate the Mount (-m), UTS/Hostname (-u), IPC (-i), Network (-n), and PID (-p) namespaces. We also use -f to fork the process.
Once the namespaces are created, the command will immediately execute chroot to trap the process inside our OverlayFS, and finally execute /bin/sh.
sudo unshare -m -u -i -n -p -f --mount-proc chroot /tmp/mycontainer/merged /bin/sh
Step 4: Proving the Isolation
You are now inside the shell of your custom container. Let’s prove that the kernel isolation is working.
1. Verify PID Isolation:
ps aux
You will only see two processes: /bin/sh (which is magically PID 1) and the ps aux command itself. You cannot see any of the host’s processes.
2. Verify Network Isolation:
ip a
You will only see the loopback interface (lo), and it will be down. The container has absolutely no network access to the host or the internet.
3. Verify Hostname Isolation:
hostname my-custom-container
hostname
You can change the hostname inside the container, and it will not affect the host Linux machine.
Step 5: Teardown and Ephemerality
Type exit to kill the container shell. You will be returned to your host terminal.
Because we used OverlayFS, tearing down the container is trivial. We unmount the merged directory, and optionally delete the upper directory to completely wipe the container’s state, exactly like docker rm.
sudo umount /tmp/mycontainer/merged
rm -rf /tmp/mycontainer/upper/*
Conclusion
By manually configuring Linux Namespaces and OverlayFS, the underlying magic of Docker and Kubernetes is demystified. Containers are not virtualization; they are a highly orchestrated application of standard Linux kernel security and filesystem features, providing profound isolation with absolute zero performance overhead.