Docker is weird: what building a container runtime taught one developer

A blogger on codeandcake.dev published a short post on Oct 22, 2026 about the odd things he found in Docker while building his own container runtime for fun. The author does not give a name, and says he is still early in the project.

His first point is that containers are not real in the way virtual machines are. Unlike VMs, a container is just a process with special restrictions on it, and the host kernel is still running that process. If you know where to look, you can find it in /proc. There is also no single Linux feature called "containers". The author says a container is really 5 different Linux kernel features in a trenchcoat. Namespaces hide what the host and the container can see, cgroups limit the container's resource usage, and there is a "whole bunch of networking magic" that isolates a container's network, which the author says he does not understand.

Second, images are just folders. Docker puts the folder somewhere and then calls chroot (or pivot_root) to make it the new root directory of the container process. So when you run docker run -it --rm ubuntu /bin/bash, Docker downloads the ubuntu image from Dockerhub, puts it somewhere, and runs a bash binary from some path inside Docker's own storage (the post writes it as /some/weird/docker/path/bin/bash).

Third, the author calls Go "kind of a terrible language for a container runtime". Docker's runtime, runc, is written in Go, whose runtime is natively multi-threaded. That is a nice feature in general, but it causes trouble here. Many of the Linux syscalls a container runtime must make have to happen in a single-threaded environment, so runc includes what the author calls a crazy C module that runs before Go's runtime initializes. Go also has no fork; a fork copies only the main thread, which the author calls a footgun in a runtime like Go's. A container runtime wants to fork, set up restrictions, and then exec the process named in docker run. To approximate that, runc execs /proc/self/exe with a special command-line argument that marks the new process as a child.

Fourth, Docker requires root. The author notes this is easy to miss if you run Docker on MacOS. Many Linux kernel APIs that container runtimes use require root, or at least used to, so the Docker daemon usually runs as root. The author adds that he can only imagine container security is a very tough job. Things seem better these days, with one big exception: networking. A rootless container runtime looks like it has to reimplement the whole networking stack in userspace to offer the same features.

The post closes on a personal note. After only starting his runtime, the author feels he has learned a lot about Docker, and recommends writing your own version of a tool you use as a way to learn it.

Key facts

  • The author, building his own container runtime for fun and still early in the project, says a container is just a process with restrictions, still run by the host kernel and visible in /proc.
  • He says containers are really 5 Linux kernel features in a trenchcoat; he names namespaces (visibility), cgroups (resource limits) and network isolation, which he says he does not understand.
  • Images are folders that Docker places somewhere and then makes the container's root directory with chroot or pivot_root.
  • runc, Docker's runtime, is written in Go, so it needs a C module that runs before Go's runtime starts, and it execs /proc/self/exe to approximate a fork.
  • The Docker daemon usually runs as root because many kernel APIs need it; rootless runtimes seem better now but look like they must reimplement networking in userspace.

Why it matters

Docker is something many developers use daily without seeing what is underneath. The post strips it down: a container is an ordinary process, an image is a directory used as a root filesystem, and the isolation comes from kernel features rather than from a virtual machine. It also shows why a Go-based runtime like runc needs awkward workarounds, such as a C module that runs before Go starts and a re-exec of /proc/self/exe in place of fork. There is no new product or research here; the value is in the explanation.

Who it affects

Developers who run Docker but have never looked at how it works, especially those on MacOS, where the author says the root requirement is easy to miss. It also speaks to anyone curious about writing a container runtime, and to people who think about container security, which the author calls a very tough job.

How to use it

The practical advice is the author's closing point: writing your own version of a tool you use is a great way to learn about it. The post also gives a mental model to check against: run docker run -it --rm ubuntu /bin/bash and Docker downloads the ubuntu image from Dockerhub, stores it, and runs bash from inside the stored folder, which becomes the process's root directory. The author says that with the right knowledge you can find the running container's process in /proc.

How solid is it

This is an informal first-person blog post, not a technical specification. The author is open about being early in the project and says he does not understand the networking part. The claims about runc, Go, root requirements and rootless networking are his own observations, and several are hedged in the text with phrases such as "seems" and "looks like". The post gives no benchmarks, version numbers or measurements.

Risks and caveats

Treat the post as a learner's notes, not a verdict on Docker. The author does not say Docker is bad; "weird" here means surprising. His remark that Go is "kind of a terrible language" for a container runtime is his opinion. On rootless operation, he says things seem better these days but that networking is the big exception, and he writes only that a rootless runtime looks like it must reimplement the networking stack in userspace. No reaction from Docker or runc maintainers is given.

“Writing your own version of a tool you use is a great way to learn about it!”

— Author of the codeandcake.dev post "Docker is weird"