sn sysnerdunderstand it from the kernel up
← curriculum map
Docker
Core · ~60 min · 5 labs
map / The stack / Docker
The stack Core ~60 min requires: Linux

Docker

There is no Docker engine magic. Every feature maps to a Linux primitive you already built by hand — this module makes each mapping explicit.

Before you start. Install Docker Engine on a real Linux host (sudo apt install -y docker.io) and add yourself to the docker group, or prefix commands with sudo.
Q1A container is just a host processwarm-up

Docker doesn't run a mini-VM. It runs an ordinary Linux process with its own namespaces.

Task

Start a container and find its process — and its network namespace — on the host.

Verify it yourself
verify
$ sudo ls -l /proc/$(docker inspect -f "{{.State.Pid}}" web)/ns/net

The container is a normal PID on your host with its own net namespace link. Exactly the unshare world you built in Linux.

Reveal solution
solution
$ docker run -d --name web nginx
$ docker inspect -f "{{.State.Pid}}" web
$ sudo ls -l /proc/$(docker inspect -f "{{.State.Pid}}" web)/ns/net
Q2Layers are overlayfscore

An image is stacked read-only layers; the container adds a thin writable layer on top — via overlayfs (the storage abstraction from the Storage module).

Task

Inspect an image's layers and see overlay mounts on the host.

Verify it yourself
verify
$ mount | grep overlay | head

You'll see overlay mounts with lowerdir/upperdir — the layered rootfs, made real.

Reveal solution
solution
$ docker image inspect nginx -f "{{.RootFS.Layers}}"
$ mount | grep overlay | head
Q3Publishing a port is DNATcore

-p 8080:80 isn't magic routing — it's an iptables DNAT rule (the netfilter you met in Linux) rewriting the destination to the container.

Task

Publish a port and find the DNAT rule it created.

Verify it yourself
verify
$ sudo iptables -t nat -L DOCKER -n | grep 8080

A DNAT rule maps host :8080 to the container's :80. That's the whole trick.

Reveal solution
solution
$ docker run -d -p 8080:80 nginx
$ sudo iptables -t nat -L DOCKER -n | grep 8080
Sponsored

Reach engineers who read the man page

Native, contextual, no tracking — this is how the curriculum stays free.

Q4docker0 is the bridge you builtcore

Docker's default network is a Linux bridge named docker0 — the same object you assembled by hand in the Linux module.

Task

Look at docker0 and the container IPs on it.

Verify it yourself
verify
$ ip addr show docker0

docker0 has a subnet; each container gets an IP on it via a veth pair. Q3–Q6 of the Linux module, automated.

Reveal solution
solution
$ ip addr show docker0
$ docker network inspect bridge -f "{{range .Containers}}{{.Name}} {{.IPv4Address}}{{end}}"
Q5Break-and-fix — the instant exitdebug

The app in a container is PID 1. If PID 1 exits, the container stops. A container with no long-running foreground process dies immediately.

Task

Watch a container exit instantly, then make it stay up.

Verify it yourself
verify
$ docker ps --filter name=stayup

docker run alpine exits at once (nothing to keep PID 1 alive). Give it a foreground process and it stays. This is the OS "app as PID 1" idea, biting.

Reveal solution
solution
$ docker run --name gone alpine        # exits immediately
$ docker run -d --name stayup alpine sleep 1000
$ docker ps --filter name=stayup
What you just built

Namespaces, cgroups, overlayfs, veth, DNAT — Docker is those five primitives with good ergonomics. Nothing you didn't already build by hand. Next: Kubernetes orchestrates thousands of these.