What Building a Container Runtime Teaches You About Docker

Original: Docker is weird

Why This Matters

Low-level container internals rarely get plain-language treatment; this fills that gap.

A developer building a custom container runtime from scratch shares surprising findings: containers are just restricted Linux processes, images are folders, Go is an awkward fit for runtimes, and Docker typically requires root.

A developer at Code and Cake decided to build their own container runtime for fun, and the early lessons are genuinely illuminating. First: containers aren't VMs. They're ordinary Linux processes with restrictions applied—still visible in /proc if you know where to look. There's no single 'containers' kernel feature; instead, the abstraction is stitched together from namespaces (visibility isolation), cgroups (resource limits), and a pile of networking primitives the author admits they don't fully grasp yet.

Images, meanwhile, are just folders. Docker downloads one, stashes it somewhere, then calls chroot or pivot_root to make that folder the process's new root directory. Simple in retrospect.

On the language side, Go—the language behind Docker's reference runtime runc—has some awkward characteristics for this use case. Its runtime is natively multi-threaded, but many Linux syscalls a container runtime needs must run single-threaded. runc works around this with a C module that runs before Go initializes. Go also lacks fork(), so runc approximates it by exec-ing /proc/self/exe with a special flag to signal child-process behavior.

Finally, Docker typically needs root because the underlying kernel APIs require it—something easy to miss on macOS. Rootless runtimes exist but must reimplement the entire networking stack in userspace to match feature parity.

Source

codeandcake.dev — Read original →