Theory: Docker architecture
Docker uses a client-server architecture. The tool you type commands into (the client) is separate from the process that actually runs containers (the daemon). They communicate over a REST API, which means the client and daemon do not have to be on the same machine.
Full architecture
flowchart TB
subgraph DC["Docker Client"]
CLI["docker CLI"]
end
subgraph DE["Docker Engine"]
D["dockerd (Docker Daemon)"]
C["containerd"]
S["containerd-shim"]
R["runc"]
end
REG[("Registry: Docker Hub / private")]
K["Linux kernel: namespaces + cgroups"]
CLI -->|"REST API"| D
D <-->|"push / pull"| REG
D --> C
C --> S
S --> R
R --> K
Component roles
- docker CLI is the client you type commands into. It sends requests to the daemon over a REST API, either on the same machine via a Unix socket or over the network.
- dockerd (Docker daemon) receives those requests and manages images, containers, networks, and volumes. It exposes the Docker Engine API.
- Registry stores and serves images. Docker Hub is the public default. Teams use private registries (AWS ECR, GitHub Container Registry, and others) to control who can push and pull.
docker pulldownloads from the registry;docker pushuploads to it. - containerd handles the container lifecycle on behalf of the daemon. It pulls image layers, manages container state, and delegates process creation.
- containerd-shim keeps each container process running independently from the daemon so a daemon restart does not kill running containers.
- runc creates the container by calling into the Linux kernel. It sets up namespaces and cgroups and starts the application process.
- Linux kernel enforces isolation (namespaces: what the process can see) and resource limits (cgroups: how much CPU and memory it can use).
What happens on docker run
- You type
docker run. The CLI sends a request to dockerd via REST API. - dockerd checks the local image store. If the image is not there it pulls it from the registry.
- dockerd calls containerd to create the container.
- containerd calls runc via a shim. runc asks the kernel to set up namespaces and cgroups, then starts your process.
What happens on docker build and push
docker build sends your Dockerfile and files to the daemon, which creates image layers and stores the result locally. docker push then uploads those layers to the registry so other machines can pull them.
Relevance to security
- Misconfiguration can enter at the CLI (
--privileged, volume mounts), in dockerd settings, or in daemon.json on the host. - The registry is on the trust path. Whoever controls push access to a registry can affect what every downstream host pulls and runs.
- containerd and runc bugs have been part of past container escape incidents. Patching the host runtime matters as much as patching the app image.