Build1 distinct publisher3 min readPublished
A walkthrough builds one with unshare, chroot and an Alpine tarball, no daemon involved. The useful part is what it tells you the day Docker is not on the box and root is.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Ordering is the part that bites. cgroup membership is a property of the process, not of the namespace, so the write into /sys/fs/cgroup/mybundle/cgroup.procs has to land before you exec into the namespaced shell, or be done with the container's PID the moment unshare --fork returns it [12][13]. Get it backwards and you have a shell sitting in fresh namespaces under the host's limits, which behaves identically right up to the point where something allocates.
The /proc flag in that one-liner is doing more work than it looks like. A new PID namespace changes the numbering the kernel hands out, but ps reads /proc, and /proc is a mount. Without --mount-proc the process keeps the host's, and your PID 1 shell lists the host's process table [8]. That is the shape of most half-isolation bugs: namespaces isolate view, cgroups limit consumption [14], and the view can be correct in the kernel while the file the tool actually reads still points elsewhere.
On the filesystem, the walkthrough is honest about its own first draft. chroot relabels a path and leaves the process attached to the host's mount tree, and it is escapable [10]. pivot_root swaps the root mount outright, and after the lazy unmount and rmdir of /oldroot there is no path left to walk back out through, which is what runc does [11]. Both recipes are given. One of them is a boundary.
Count the namespaces in the unshare line and you get five: mount, uts, ipc, net and pid [1]. Nothing in it remaps user IDs, and every command in the post runs under sudo [17]. That makes it a sealed rootfs for a build you wrote, not a fence around code you did not.
What you do not get is reachability. The net namespace comes up with loopback and nothing else [9], and the supplied text stops at the sentence where it would have wired real connectivity [18]. For the locked-down runner case that is the live question, because a build that cannot fetch a dependency is not a build, and the rootfs itself is a tarball you have to have pulled already [5]. The offline half still works: a directory tree, five namespaces, a memory and process cap, and no registry or daemon involved [2].
That is also the argument for doing it by hand once. The author's framing is that this is the mental model for the OCI runtime spec, runc, crun, or a sandbox you write yourself [16], and the bundle directory you built is the same rootfs-plus-config layout the spec formalises [6]. It costs one tarball and a handful of commands to learn which pieces Docker assembles on your behalf [1], and which of them are already sitting on a machine where dockerd is not [4].
Ranked by verification strength, evidence, and original report placement.
The walkthrough builds a minimal container runtime by hand with no dockerd and no containerd, using only unshare, chroot and a plain directory tree called a bundle, which is roughly what runc does under the hood when Docker runs a container.
Docker is described as a thick layer of abstraction sitting on top of a handful of Linux kernel primitives, consisting of the daemon, the CLI and the image registry.
Stripped to primitives, a container is a process with restricted namespaces (its own view of PIDs, mounts, network, hostname), a root filesystem it has been chroot'd or pivot_root'd into, and optionally cgroups limiting what it can consume.
unshare lets you start a process in new namespaces without any daemon involved.
The command given is: sudo unshare --mount --uts --ipc --net --pid --fork --mount-proc chroot ~/mybundle/rootfs /bin/sh, where --mount gives its own mount table, --uts its own hostname, --ipc isolated System V IPC and POSIX message queues, --pid --fork a new PID namespace in which the shell becomes PID 1, and --net its own network stack.
--mount-proc remounts /proc inside the new mount namespace so that ps reports container-local PIDs instead of the host's.
Follow any of these and your For You feed starts watching them — no settings page required.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Self-consistent, reproducible commands from one hands-on post; no independent corroboration
The technical claims are specific and directly checkable — exact unshare flags, the Alpine 3.20.3 minirootfs URL, the pivot_root sequence, and the cgroup v2 writes — which raises evidentiary quality above assertion-level. But everything rests on a single dev.to author with no command output, no tested kernel/distro versions, no second source, and two ledger readings that the same body contradicts (sudo-everywhere, and the alleged absence of a networking recipe). That caps evidence near the midpoint.
No adoption data in cluster
The cluster contains one tutorial and no release, deployment, benchmark, pricing, licensing or usage-disclosure evidence. The underlying primitives (namespaces, cgroups, runc, the OCI runtime spec) are referenced but the source supplies no figures on who uses this hand-rolled approach, so no adoption score can be measured without inference.
Mildly overstated equivalence to Docker, largely self-corrected
Two framings lean beyond what is shown: that the cgroup writes enforce limits 'exactly like' docker run, and that after pivot_root the process 'genuinely cannot see the host filesystem at all' — while the demonstrated path still uses plain chroot, runs as root with no user namespace, and adds no seccomp. The author offsets much of this by explicitly listing what is missing (image layering, registry client, seccomp) and by conceding the networking section amounts to reimplementing Docker's stack, so the gap is small and positive rather than large.
Community-platform authorship; no vendor or commercial stake visible
The single source is an individual post on dev.to explaining kernel primitives and open-source tooling (unshare, chroot, runc, crun, skopeo, umoci, Alpine). Nothing is being sold, licensed or benchmarked against a competitor, and no sponsor, employer or product is disclosed. The residual incentive is the ordinary one for community tutorials: audience and reputation, which favours a crisp 'Docker is just X' framing over caveats.
Moderate-low: one publisher, verifiable mechanics, two contested ledger readings
Confidence is limited by single-publisher coverage and the absence of any adoption evidence, and further by two ledger claims that the source body contradicts. It is not lower because the subject matter is stable, well-documented kernel behaviour and the commands are specific enough to be independently reproduced by a reader with root on a modern Linux host.
build
912MB to 108MB is mostly typing now, and that weakens the base-image excuse in review1 distinct publisher
build
Before you spend quota on an agent skill, make it pass an eval harness1 distinct publisher
build
The merge gate that turns "works on bad networks" into something CI can fail1 distinct publisher
build
752 Nigerian institutions, shipped as a repo instead of an endpoint1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 27, 2026