I’ve been using Docker for a while, but at some point I realized I didn’t actually understand what it was doing under the hood.
How does a process suddenly get its own filesystem, its own network, its own environment?
So instead of reading more blogs, I decided to build it myself.
No Docker. No containerd. No LXC.
Just namespaces, cgroups, networking, and filesystem isolation.
That’s how I ended up building LXR (Linux Container Runtime) written in Go, using only Linux primitives.
The idea
Build containers from scratch and make them usable for real development.
LXR not only creates containers, but also lets you work inside them directly from the browser or terminal, while the runtime handles everything underneath.
So LXR can:
- pull images from Docker Hub
- extract rootfs
- create isolated containers
- configure networking (veth + bridge + IP allocation)
- provide shell access via lxr exec
- run code-server inside containers for browser access
Using it
From the outside, it looks simple:
lxr create --name goCon golang
But internally, this triggers a full setup.
What actually happens:
- image layers are pulled
- dependencies are installed into the image rootfs
- root filesystem is prepared
- overlay filesystem is created
- networking is configured
- IP is allocated
- container process is started
- code-server is launched
All of this... just to start one container.
Result
Once containers are running:
lxr ps
Each container has:
- its own PID
- its own IP
- its own network namespace
- its own filesystem (OverlayFS)
At this point, LXR behaves as an real container runtime.
The first problem
The first issue wasn’t networking or filesystem.
It was just starting a process.
I tried running containers as a non-root user, but it didn’t work the way I expected.Processes failed to start or behaved inconsistently due to restrictions.
This turned out to be related to how Linux handles namespaces and permissions.
I’ll cover that part in the upcoming post.




Top comments (0)