Chapter 11 Docker Runtime & Networking
Running containers and reading how they end, the debugging toolkit, signals and restart policies, limits and the JVM, networks and published ports, volumes and the UID problem, logs that fill disks, container hardening, and Compose.
In plain words
Chapter 10 taught you to pack a lunchbox: the image, with everything the app needs sealed inside. This chapter is about lunchtime itself. Where does the box sit, who can pass it things, how much table space does it get, what happens when it is taken away, and what does it leave behind on the table?
A running container is an ordinary Linux process that the kernel has put in a few boxes at once: its own view of the network (namespaces), a budget of memory and CPU (cgroups), a trimmed list of root powers (capabilities), and a filesystem stacked from image layers. docker run sets all of that up; docker ps, logs, inspect, stats and events let you look inside. Everything here reuses chapters 1 to 10 - signals, cgroups, permissions, ports, DNS - just wearing a container.
Why it matters on call
This is where "it works on my machine" turns into "it runs on the server". The pages you will get are runtime pages: a container that restarts every few seconds with exit 137, a service that cannot reach db although both containers are up, a disk at 100% because one container's JSON log was never rotated, a volume that suddenly looks empty after a redeploy.
Every one of those has a two-command diagnosis once you know where to look. And none of it is new magic: exit 137 is the cgroup OOM from chapter 5, restart policies are systemd's Restart= from chapter 2, "cannot reach db" is the DNS and port method from chapters 8 and 9. Learning them on one Docker host, where you can see every file, process and cgroup, means every later platform that runs containers is the same ideas at a bigger scale.
Lessons
- Running containers, and reading how they ended
- The debugging toolkit: logs, exec, inspect, events, stats
- Stopping, killing and restarting
- Memory, CPU and PIDs: the cgroups again
- Networks: bridges, DNS, published ports, and localhost
- Debugging container networking, layer by layer
- Volumes, bind mounts, tmpfs, and the UID problem
- Logging drivers, and the log file that fills the disk
- Container security: capabilities, read-only, and the socket
- Docker Compose: several containers as one project
25 hands-on labs (missions, incidents and drills) run in the terminal: Open this chapter in the terminal Free, in your browser - a real Ubuntu terminal to try it in, with missions that check your work.
Questions people ask
Is a container a small virtual machine?
No. A VM boots its own kernel on virtual hardware. A container is a normal process on the host's kernel, with namespaces limiting what it can see and cgroups limiting what it can use. That is why it starts in milliseconds, why ps on the host shows the container's processes, and why a kernel bug or a mounted Docker socket can reach the host. It also means the container shares the host's kernel version: an arm64 host runs arm64 images.
How does this chapter relate to systemd from chapter 2?
Closely. dockerd is itself a systemd unit, and a container is supervised much like a service: a restart policy is Restart=, the stop grace period is TimeoutStopSec, -m and --cpus write the same cgroup files as MemoryMax and CPUQuota, and --cap-drop or no-new-privileges are the hardening directives from 2.26. The difference is packaging: the container brings its own filesystem from an image, and runs in its own namespaces.
Is Docker the only way to run containers?
No. Docker is the most common tool, but under dockerd there is containerd and runc, and other engines such as Podman run the same images without a root daemon. What you learn here about PID 1, cgroups, capabilities and network namespaces is about Linux, not about the Docker brand, so it carries over to any of them. The commands differ a little; the kernel features underneath are identical.
Why does the container's process show up in the host's ps?
Because it is a host process. The PID namespace gives it its own numbering inside (it sees itself as PID 1), but the host kernel tracks it under a normal host PID. docker top and docker inspect -f '{{.State.Pid}}' show that host PID, which is what you use with nsenter, /proc/<pid>/ and strace. Users work the same way: uid 1000 inside is uid 1000 outside unless user namespaces remap it.
What is the order to debug a broken container?
Status first, then output, then facts. docker ps -a shows whether it exited, restarts or is unhealthy, and the exit code. docker logs shows what PID 1 said. docker inspect gives OOMKilled, the real environment, mounts, networks and limits. docker events puts it in time order. If it is a connectivity problem, switch to the four-step network method from inside the caller's namespace. Only then change something.