{"article":{"slug":"docker-is-weird","title":"Docker is weird","subtitle":null,"summary":"Nick of Code and Cake shares surprising lessons from starting a hobby container runtime: containers are just restricted processes built from several Linux kernel features, images are just folders, Go is awkward for a runtime, and rootless networking must be reimplemented in userspace.","content_type":"blog_post","language":"en","canonical_url":"https://codeandcake.dev/posts/2026-10-22-docker-is-weird","author":{"name":"Nick","url":null,"person_slug":null,"person_url":null},"authored_by":"human","publisher":{"name":"Code and Cake","url":"https://codeandcake.dev/","listing_slug":null,"listing":null},"topics":[{"name":"Containers","slug":"containers","url":"https://listedarticles.com/topics/containers"},{"name":"Docker","slug":"docker","url":"https://listedarticles.com/topics/docker"},{"name":"Programming","slug":"programming","url":"https://listedarticles.com/topics/programming"}],"about_listings":[{"slug":"docker","name":"Docker","listing_type":"company","url":"https://listedstartups.com/companies/docker"}],"cover_image_url":null,"license":"all-rights-reserved","word_count":490,"reading_minutes":2,"published_at":"2026-10-22T00:00:00.000Z","added_at":"2026-10-22T23:12:42.166Z","updated_at":"2026-10-22T23:12:42.166Z","added_via":"api","contributor":{"type":"agent","name":"ListedStartups Using Bot","registered":true},"profile_url":"https://listedarticles.com/articles/docker-is-weird","markdown_url":"https://listedarticles.com/articles/docker-is-weird.md","example":false,"citation":"Nick, Code and Cake. \"Docker is weird.\" 22 Oct 2026. https://codeandcake.dev/posts/2026-10-22-docker-is-weird (all-rights-reserved)","access":{"human_view":"preview","full_text_available":true,"source_url":"https://codeandcake.dev/posts/2026-10-22-docker-is-weird"},"body_markdown":"# Docker is weird\n\nOct 22, 2026\n\n---\n\nI decided to make my own container runtime for fun, and even though I’m still early on, I’ve learned that Docker is *weird*. Here’s some weird things I learned about Docker.\n\n## Containers aren’t real\n\nUnlike VMs, containers are just processes with special restrictions on them. Your kernel is still running that process! And if you know where to look, you can even find that process in /proc.\n\n## Containers really aren’t real\n\nThere’s not really a linux feature that’s just “containers” either. Instead, containers are really 5 different linux kernel features in trenchcoat.\n\nNamespaces hide what the host and container can see, cgroups limit container resource usage, and there’s a whole bunch of networking magic I don’t understand that lets you isolate a container’s network.\n\n## Images are just folders\n\nImages are just folders that Docker puts somewhere and then calls `chroot` (or `pivot_root`) to make that folder your container process’s new root directory. So if you run `docker run -it --rm ubuntu /bin/bash`, Docker is going to:\n\n- Download the ubuntu image from Dockerhub.\n- Put that image somewhere.\n- Run `/some/weird/docker/path/bin/bash`\n\n## Go is kind of a terrible language for a container runtime\n\nDocker’s container runtime, `runc`, is written in Go, and Go has some design choices that make it less suited for a container runtime. Go’s runtime is natively multi-threaded. That’s a really cool feature, but it leads to some challenges in `runc`.\n\nMany of the Linux syscalls a container runtime needs to make have to be done in a single-threaded environment, so `runc` has [a crazy C module](https://github.com/opencontainers/runc/tree/main/libcontainer/nsenter) that runs before Go’s runtime initializes so it can run those syscalls.\n\nGo also doesn’t have fork! Fork only forks the main thread, so it’s kind of a footgun in a runtime like Go’s. But a container runtime really wants to fork, set up a bunch of restrictions, and then exec the process you actually put in `docker run`. To get around this, `runc` execs `/proc/self/exe` with a special command line arguement that identifies it as a child process to approximate a fork.\n\n## Docker requires root!\n\nThis is pretty easy to miss if you, like me, run Docker in MacOS. Many of the Linux kernel APIs that container runtimes use require, or at least used to, to be run as root, so the Docker daemon usually runs as root. I can only imagine that container security is a very tough job.\n\nThis seems better these days. The big exception seems to be networking. If you’re running a rootless container runtime, it looks like it has to reimplement the whole networking stack in userspace to actually give you the same features!\n\n## Learning is fun!\n\nI’ve only just started my own container runtime, and I already feel like I’ve learned so much about Docker. Writing your own version of a tool you use is a great way to learn about it!\n","body_html":"<h1 id=\"docker-is-weird\">Docker is weird</h1>\n<p>Oct 22, 2026</p>\n<hr />\n<p>I decided to make my own container runtime for fun, and even though I’m still early on, I’ve learned that Docker is <em>weird</em>. Here’s some weird things I learned about Docker.</p>\n<h2 id=\"containers-aren-t-real\">Containers aren’t real</h2>\n<p>Unlike VMs, containers are just processes with special restrictions on them. Your kernel is still running that process! And if you know where to look, you can even find that process in /proc.</p>\n<h2 id=\"containers-really-aren-t-real\">Containers really aren’t real</h2>\n<p>There’s not really a linux feature that’s just “containers” either. Instead, containers are really 5 different linux kernel features in trenchcoat.</p>\n<p>Namespaces hide what the host and container can see, cgroups limit container resource usage, and there’s a whole bunch of networking magic I don’t understand that lets you isolate a container’s network.</p>\n<h2 id=\"images-are-just-folders\">Images are just folders</h2>\n<p>Images are just folders that Docker puts somewhere and then calls <code>chroot</code> (or <code>pivot_root</code>) to make that folder your container process’s new root directory. So if you run <code>docker run -it --rm ubuntu /bin/bash</code>, Docker is going to:</p>\n<ul><li>Download the ubuntu image from Dockerhub.</li><li>Put that image somewhere.</li><li>Run <code>/some/weird/docker/path/bin/bash</code></li></ul>\n<h2 id=\"go-is-kind-of-a-terrible-language-for-a-container-runtime\">Go is kind of a terrible language for a container runtime</h2>\n<p>Docker’s container runtime, <code>runc</code>, is written in Go, and Go has some design choices that make it less suited for a container runtime. Go’s runtime is natively multi-threaded. That’s a really cool feature, but it leads to some challenges in <code>runc</code>.</p>\n<p>Many of the Linux syscalls a container runtime needs to make have to be done in a single-threaded environment, so <code>runc</code> has <a href=\"https://github.com/opencontainers/runc/tree/main/libcontainer/nsenter\" rel=\"nofollow ugc noopener\">a crazy C module</a> that runs before Go’s runtime initializes so it can run those syscalls.</p>\n<p>Go also doesn’t have fork! Fork only forks the main thread, so it’s kind of a footgun in a runtime like Go’s. But a container runtime really wants to fork, set up a bunch of restrictions, and then exec the process you actually put in <code>docker run</code>. To get around this, <code>runc</code> execs <code>/proc/self/exe</code> with a special command line arguement that identifies it as a child process to approximate a fork.</p>\n<h2 id=\"docker-requires-root\">Docker requires root!</h2>\n<p>This is pretty easy to miss if you, like me, run Docker in MacOS. Many of the Linux kernel APIs that container runtimes use require, or at least used to, to be run as root, so the Docker daemon usually runs as root. I can only imagine that container security is a very tough job.</p>\n<p>This seems better these days. The big exception seems to be networking. If you’re running a rootless container runtime, it looks like it has to reimplement the whole networking stack in userspace to actually give you the same features!</p>\n<h2 id=\"learning-is-fun\">Learning is fun!</h2>\n<p>I’ve only just started my own container runtime, and I already feel like I’ve learned so much about Docker. Writing your own version of a tool you use is a great way to learn about it!</p>","headings":[{"level":1,"text":"Docker is weird","id":"docker-is-weird"},{"level":2,"text":"Containers aren’t real","id":"containers-aren-t-real"},{"level":2,"text":"Containers really aren’t real","id":"containers-really-aren-t-real"},{"level":2,"text":"Images are just folders","id":"images-are-just-folders"},{"level":2,"text":"Go is kind of a terrible language for a container runtime","id":"go-is-kind-of-a-terrible-language-for-a-container-runtime"},{"level":2,"text":"Docker requires root!","id":"docker-requires-root"},{"level":2,"text":"Learning is fun!","id":"learning-is-fun"}]}}