sn sysnerdunderstand it from the kernel up
← curriculum map
Linux
Keystone · ~120 min · 8 labs
map / Implementation / Linux
Implementation Keystone ~120 min requires: Operating Systemsrequires: Networkingrequires: Storage

Linux

This is the keystone. Everything from Concepts becomes concrete here, and every tool above decomposes back into it. You'll hand-build the two things that make a container a container — isolation and limits — and then wire container networking from scratch.

Before you start. You need a real Linux kernel with sudo. On native Ubuntu you're set. On macOS or Windows, don't use Docker Desktop or WSL2 for these labs — the kernel networking pieces break there. Spin up a real VM: multipass launch --name lab then multipass shell lab.
Q1Build a container with your bare handscore

A container is a normal process wearing two costumes: namespaces (what it can see) and cgroups (what it can use). unshare puts on the namespace costume.

Task

Enter a new PID + mount namespace so the process becomes PID 1 and can only see itself.

Verify it yourself
verify
$ echo $$   # inside the new shell

Inside, echo $$ prints 1 and ps aux shows only your own processes. You are PID 1 of a private process world — the core of a container.

Reveal solution
solution
$ sudo unshare --pid --fork --mount-proc bash
# now inside:
$ echo $$
$ ps aux
Q2Cap its memory with a cgroup (meet the OOM killer)core

Namespaces isolate; cgroups limit. cgroup v2 lives in /sys/fs/cgroup. Set a memory ceiling and the kernel enforces it by killing the offender.

Task

Create a cgroup capped at 50 MB, put a shell in it, and try to allocate 200 MB.

Verify it yourself
verify
$ cat /sys/fs/cgroup/demo/memory.events

The allocation is Killed instantly and oom_kill increments. The bouncer threw out your process — but only inside its cgroup, not the whole host.

Reveal solution

In production, systemd-run --scope -p MemoryMax=50M yourcmd is the one-liner form of all this.

solution
$ echo +memory | sudo tee /sys/fs/cgroup/cgroup.subtree_control
$ sudo mkdir -p /sys/fs/cgroup/demo
$ echo 50M | sudo tee /sys/fs/cgroup/demo/memory.max
$ echo $$ | sudo tee /sys/fs/cgroup/demo/cgroup.procs
$ python3 -c "x='A'*(200*1024*1024); import time; time.sleep(60)"
$ cat /sys/fs/cgroup/demo/memory.events
Q3Prove a network namespace is emptycore

A container's network stack is a network namespace — a private, nearly empty copy of interfaces and routes.

Task

Create a namespace net1 and look inside.

Verify it yourself
verify
$ sudo ip netns exec net1 ip link show

Only lo, and it's DOWN. That empty stack is your blank container network.

Reveal solution
solution
$ sudo ip netns add net1
Sponsored

Reach engineers who read the man page

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

Q4Build the bridge (this is docker0)core

To connect multiple namespaces you need a virtual switch — a bridge. This is precisely what docker0 is. Give it 10.10.0.1 as the gateway.

Task

Create bridge br0, address it, bring it up.

Verify it yourself
verify
$ ping -c1 10.10.0.1

0% packet loss — the bridge IP now lives on your host.

Reveal solution
solution
$ sudo ip link add br0 type bridge
$ sudo ip addr add 10.10.0.1/24 dev br0
$ sudo ip link set br0 up
Q5Plug a namespace into the bridgecore

A veth pair is a virtual cable: one end in the namespace, the other on the bridge. Exactly the wiring Docker makes per container.

Task

Wire net1 to br0 as 10.10.0.2/24, bring loopback up, and add a default route via the gateway.

Verify it yourself
verify
$ sudo ip netns exec net1 ping -c1 10.10.0.1

0% packet loss — the namespace reaches its gateway across the bridge you built.

Reveal solution
solution
$ sudo ip link add veth1 type veth peer name veth1-br
$ sudo ip link set veth1 netns net1
$ sudo ip link set veth1-br master br0
$ sudo ip link set veth1-br up
$ sudo ip netns exec net1 ip link set lo up
$ sudo ip netns exec net1 ip addr add 10.10.0.2/24 dev veth1
$ sudo ip netns exec net1 ip link set veth1 up
$ sudo ip netns exec net1 ip route add default via 10.10.0.1
Q6Two namespaces talking — the payoffcore

Add a second namespace on the same bridge and they can talk — through a switch you built by hand. You just recreated container-to-container networking.

Task

Create net2 as 10.10.0.3/24 on br0, then ping between the two.

Verify it yourself
verify
$ sudo ip netns exec net1 ping -c1 10.10.0.3

0% loss both ways. This is a Docker bridge network, assembled from primitives.

Reveal solution
solution
$ sudo ip netns add net2
$ sudo ip link add veth2 type veth peer name veth2-br
$ sudo ip link set veth2 netns net2
$ sudo ip link set veth2-br master br0
$ sudo ip link set veth2-br up
$ sudo ip netns exec net2 ip link set lo up
$ sudo ip netns exec net2 ip addr add 10.10.0.3/24 dev veth2
$ sudo ip netns exec net2 ip link set veth2 up
$ sudo ip netns exec net2 ip route add default via 10.10.0.1
Q7Break-and-fix — the dangling cabledebug

Paste the setup: it builds net3 that should work but can't reach the gateway. Diagnose it.

Setup — paste to create the broken state
setup
sudo ip netns add net3
sudo ip link add veth3 type veth peer name veth3-br
sudo ip link set veth3 netns net3
sudo ip netns exec net3 ip link set lo up
sudo ip netns exec net3 ip addr add 10.10.0.4/24 dev veth3
sudo ip netns exec net3 ip link set veth3 up
sudo ip netns exec net3 ip route add default via 10.10.0.1
# (something is missing on the host side...)
Task

The namespace side looks complete. Check the host end of the cable — ip link show veth3-br and bridge link.

Verify it yourself
verify
$ sudo ip netns exec net3 ping -c1 10.10.0.1

0% loss once fixed.

Reveal solution

The host end was never enslaved to the bridge or brought up — the cable dangled. The most common real container-networking fault: the pod end is fine, the host veth isn't attached.

solution
$ sudo ip link set veth3-br master br0
$ sudo ip link set veth3-br up
Q8Bonus — reach the internet with NATbonus

Private 10.10.0.0/24 can't reach the internet — no route back. Docker fixes this with a MASQUERADE rule. Same trick.

Task

Enable forwarding and masquerade the subnet, then ping a public IP from net1.

Verify it yourself
verify
$ sudo ip netns exec net1 ping -c1 8.8.8.8

0% loss (needs host internet).

Reveal solution

If it still fails, your host FORWARD policy may be DROP: sudo iptables -P FORWARD ACCEPT.

solution
$ sudo sysctl -w net.ipv4.ip_forward=1
$ sudo iptables -t nat -A POSTROUTING -s 10.10.0.0/24 ! -o br0 -j MASQUERADE
What you just built

Namespaces + cgroups + a rootfs = a container. Bridge + veth + NAT = docker0. You built all of it by hand, so Docker and Kubernetes are now just automation over primitives you understand. Clean up with sudo ip netns del net1 net2 net3; sudo ip link del br0.