Skip to content
LatchkeyLatchkey home

Docker Architecture: What Actually Runs When You Type docker run

The docker command does almost nothing. It sends an HTTP request to a daemon, which delegates to a container runtime, which asks the kernel for the isolation. Knowing which layer is which is what makes Docker failures diagnosable.

Docker is usually drawn as one box. It is at least four, and they fail differently: a CLI that only formats requests, a daemon that holds all the state, a runtime that creates the container, and kernel features that provide the isolation.

The reason this matters practically is that error messages come from different layers, and knowing the source narrows the cause immediately.

The layers, top to bottom

What happens on docker run

  • The CLI POSTs to the daemon over the Unix socket. If this fails you get "Cannot connect to the Docker daemon", which means the daemon is not running, not that anything is wrong with your image.
  • The daemon checks for the image locally and asks containerd to pull it if it is missing. Registry authentication and rate limits surface here.
  • containerd unpacks the layers into a union filesystem and prepares a writable layer on top.
  • runc creates the namespaces and cgroups, pivots into the new root filesystem, and executes your process. Then runc exits and a shim holds the container.
  • The daemon streams logs and your exit code back to the CLI.

Images are layers, and that is the whole caching story

An image is an ordered stack of read-only filesystem layers plus a JSON manifest, addressed by the hash of its contents. Two images built from the same base share those layers on disk and over the network, which is why pulling a second image from the same base is fast. A running container adds one writable layer on top, and copy-on-write means a file is only duplicated into it when the container modifies it.

BuildKit builds, and it is separate

Since Docker v23 docker build runs through BuildKit rather than the old sequential builder. BuildKit parses the Dockerfile into a dependency graph, so independent stages build in parallel, and it keeps its own cache that is not part of the image store. That last point is the source of a common surprise: pruning images does not free the build cache, because they are different stores.

Why the layering matters when something breaks

Applying this to your pipeline

  • Measure before changing. Most CI optimisation targets the wrong step because the slow one is assumed rather than timed.
  • Cache what is expensive to produce and cheap to validate, and key the cache to the exact tool version.
  • Fail fast: run the cheapest checks that can reject a change first, so an expensive job never starts on code that cannot pass.
  • Prefer determinism over speed when they conflict. A fast pipeline nobody trusts gets re-run, which is slower than a slow one that is believed.

Frequently asked questions

What is the architecture of Docker?
A client-server design: the docker CLI sends requests to the dockerd daemon over a socket, the daemon delegates to containerd for image and lifecycle management, containerd calls runc to create the container, and runc configures Linux namespaces and cgroups. The kernel does the actual isolating.
What is the difference between Docker, containerd and runc?
Docker is the whole product and the daemon that manages state. containerd is the supervisor that pulls images and manages container lifecycle. runc is the low-level runtime that creates a single container and then exits. Kubernetes talks to containerd directly and skips the Docker daemon entirely.
Is a Docker container a virtual machine?
No. Containers share the host kernel and are isolated with namespaces and cgroups rather than virtualised hardware, which is why they start in milliseconds. On macOS and Windows there is a Linux VM in the picture, but only because a Linux kernel has to come from somewhere.
What does the Docker daemon actually do?
It holds all the state: images, containers, volumes, networks and the build cache. The CLI is stateless and only formats requests, which is why docker can point at a daemon on another machine by setting DOCKER_HOST.
Why does my build cache survive deleting all my images?
Because BuildKit keeps its cache in a separate store from the image store. docker image prune -a empties one and leaves the other untouched, which is why docker system df can still report tens of gigabytes after you have deleted every image.

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card