Docker Architecture
Docker uses a client-server architecture: the Docker CLI (client) converts commands into API requests and sends them to the Docker daemon (dockerd), which coordinates the actual work through a chain of specialized runtimes. The client and daemon can be on the same host or connected over a network.
The Delegation Chain
Section titled “The Delegation Chain”No single component creates a container alone. Responsibility is delegated down a chain:
Docker CLI → dockerd → containerd → shim → runc → container process| Component | Role |
|---|---|
| Docker CLI | Translates user commands into Docker API requests |
dockerd | Receives API calls, coordinates the rest — no longer creates containers directly |
containerd | High-level runtime — manages container lifecycle, converts images to OCI bundles |
| shim | Per-container process that outlives runc, keeps streams open, enables daemonless containers |
runc | Low-level OCI runtime — calls the kernel to build namespaces and cgroups, then exits |
For the full deep dive on each component — including history, internals, the step-by-step startup sequence, and Linux binaries — see Docker Engine.
Why Client-Server?
Section titled “Why Client-Server?”The separation of client and daemon is a deliberate design choice with real consequences:
- The daemon runs as root.
dockerdneeds elevated privileges to create namespaces, manage cgroups, and manipulate network interfaces. The CLI does not need to run as root — it just sends API requests. - Remote management is a first-class feature. The daemon listens on a socket; any authorized client anywhere on the network can manage it. CI runners, Portainer, VS Code extensions — they all speak the same Docker API.
- Crash isolation. Because the daemon and the container processes are decoupled via shims, a
dockerdcrash or restart does not kill running containers. The shim keeps the container alive and reconnects the daemon when it comes back.
Docker Contexts
Section titled “Docker Contexts”A context is a named configuration that points the Docker CLI at a specific Docker daemon. This lets you manage multiple Docker environments (local, remote server, Docker Desktop, cloud) from the same terminal without changing environment variables.
# List all configured contexts — the active one is marked with *docker context ls
# Switch to a different contextdocker context use my-remote-server
# Run any Docker command against the active contextdocker ps
# Create a new context pointing at a remote daemon over SSHdocker context create my-remote-server \ --docker "host=ssh://user@remote-host"
# Create a context for a TLS-secured remote daemondocker context create prod \ --docker "host=tcp://prod-host:2376,cert=~/.docker/certs/prod"
# Inspect a contextdocker context inspect my-remote-server
# Remove a contextdocker context rm my-remote-serverContexts in Practice
Section titled “Contexts in Practice”| Use Case | Context setup |
|---|---|
| Local development | Default context (Docker Desktop or Engine) |
| Remote production server | SSH context: host=ssh://deploy@prod-host |
| Multiple cloud environments | Separate context per region/account |
| CI/CD runner | Set DOCKER_CONTEXT environment variable |
# Override context for a single command without switchingdocker --context my-remote-server ps
# Or use the DOCKER_CONTEXT environment variableDOCKER_CONTEXT=my-remote-server docker psDaemonless Containers
Section titled “Daemonless Containers”A key architectural property of Docker’s shim-based design: running containers are not children of the daemon. The shim process (containerd-shim-runc-v2) becomes the parent of each container’s PID 1 after runc exits.
This means:
dockerdcan be stopped, updated, and restarted without touching running containerscontainerdcan be upgraded independently of the containers it manages- The container survives daemon crashes — the shim holds the I/O streams and re-reports status when the daemon reconnects
This design is what makes zero-downtime Docker daemon upgrades possible on production hosts.
Rootless Docker
Section titled “Rootless Docker”Docker can be run entirely without root — both the daemon and all container processes run as a non-privileged user. This is called rootless mode.
# Install rootless Docker (run as a non-root user)dockerd-rootless-setuptool.sh install
# The daemon socket is now in the user's runtime direxport DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sockdocker ps| Standard Docker | Rootless Docker | |
|---|---|---|
| Daemon privilege | Root | Non-root user |
| Container isolation | Kernel namespaces | Kernel namespaces + user namespaces |
| Performance | Full | Slight overhead from user namespace mapping |
| Feature parity | Full | Some features limited (e.g., binding privileged ports < 1024) |
| Best for | Servers, CI runners | Shared developer machines, security-sensitive environments |
Docker Desktop
Section titled “Docker Desktop”Docker Desktop is the developer-friendly distribution for macOS and Windows. It wraps Docker Engine in a Linux VM (via Apple Hypervisor / WSL2) and adds:
| Component | Purpose |
|---|---|
| CLI | Standard docker commands |
| GUI | Manage images, containers, resource limits (CPU/memory/disk) |
| Credential Helper | Secure credential storage for private registries |
| Extensions | Third-party tools (e.g., Dive, Portainer, Trivy) |
| Optional Kubernetes | Single-node K8s cluster alongside Docker |
Docker Engine vs Docker Desktop
Section titled “Docker Engine vs Docker Desktop”| Docker Engine | Docker Desktop | |
|---|---|---|
| OS | Linux only | macOS, Windows, Linux |
| License | Free (Apache 2.0) | Free for personal use; paid for large orgs |
| Kubernetes | Not included | Optional, single-node |
| GUI | None | Included |
| VM overhead | None | Yes (Linux VM layer) |
- On Linux, prefer Docker Engine for servers and CI runners — no VM overhead, full performance.
- On macOS/Windows, Docker Desktop is the practical choice. The VM boundary means bind mounts have some performance overhead compared to native Linux.
How the API Works
Section titled “How the API Works”Client and daemon communicate over a local socket by default:
- Linux:
/var/run/docker.sock - Windows:
\\.\pipe\docker_engine
# Connect CLI to a remote Docker daemondocker -H remote-host:2375 psexport DOCKER_HOST=tcp://remote-host:2375
# The CLI is just a REST API wrapper — you can call it directly:curl --unix-socket /var/run/docker.sock http://localhost/containers/jsonAny tool that speaks the Docker REST API — Portainer, VS Code Docker extension, CI runners — can manage Docker. The CLI is not special.