How to Configure Linux Namespaces and OverlayFS to Build a Custom Container Runtime

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.

  1. Namespaces (The Lie): We use the unshare system 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 sees eth0 of the container, not the host), and its own Mount namespace.
  2. 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.
  3. chroot (The Jail): Once the namespaces and filesystem are ready, we use chroot to 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.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.