Docker Architecture
Docker’s architecture is built around a clear separation between user-facing commands, container management, and low-level process execution. This modular design explains why Docker can operate as a lightweight development tool, a production runtime, and the foundation of larger orchestration platforms such as Kubernetes.
At a high level, Docker follows a client–server model with three principal components:
- Docker client — The command-line interface, Docker Compose, and graphical tools all send requests to the Docker daemon.
- Docker daemon (
dockerd) — The central engine that manages images, containers, networks, volumes, and API requests. - Container registry — A service that stores and distributes images, with Docker Hub being the best-known public registry.
These components do not have to run on the same machine. On Linux, the client usually communicates with the daemon through a UNIX socket such as /var/run/docker.sock. Remote clients can use a TCP connection, normally secured with TLS.
The separation is important: the client does not create containers directly. It translates user intent into API calls, while the daemon decides how those requests should be fulfilled.
Docker Client
The docker command-line tool is the most visible part of Docker, but it is primarily an API client. Consider the following command:
docker run -d -p 8080:80 nginx
The client parses the options, validates basic arguments, and sends a create-and-start request to dockerd. The daemon—not the client—performs the underlying work.
Docker Compose follows a similar model. A Compose file describes a set of services, networks, and volumes, while the Compose client converts that declaration into API operations. Desktop applications and automation platforms generally use the same Docker Engine API, so they interact with containers through the same control surface.
The Docker Engine and Daemon
The term Docker Engine often refers to the complete container runtime installed on a host. Its main process is dockerd, which exposes the Docker API and coordinates the lifecycle of Docker objects.
Among its responsibilities are:
- Pulling and pushing images to registries
- Creating, starting, stopping, and removing containers
- Managing networks, port mappings, and volumes
- Resolving image tags and container configuration
- Delegating execution tasks to the container runtime
- Recording logs and exposing container events
The daemon acts as the orchestration layer between high-level Docker requests and low-level operating-system primitives. Instead of configuring namespaces, filesystems, and resource limits itself, it delegates those tasks to specialized components.
containerd, the Shim, and runc
Modern Docker uses a layered runtime architecture based on the Open Container Initiative (OCI) standards.
containerd
containerd is a core container runtime responsible for managing container lifecycle tasks and handling container content such as image layers. It coordinates with Docker through internal APIs and abstracts away the details of the executable runtime.
By separating container management from process creation, Docker allows the same containerd runtime to be used directly by orchestration systems such as Kubernetes.
containerd-shim
Each container may have an associated shim process. The shim acts as an intermediary between containerd and the container’s main process.
This design provides several benefits:
- The container can continue running if
dockerdrestarts. - The runtime does not need to remain the direct parent of every container process.
- Container processes can be supervised and monitored independently.
- Runtime failures are less likely to affect unrelated containers.
runc
runc is the low-level runtime that actually creates the container process. It configures Linux kernel facilities, including namespaces and control groups, then starts the process described by the container configuration.
After creating the container, runc exits. The shim remains responsible for monitoring the container and reporting its status. This division keeps the execution path small while preserving a stable management interface.
Core Docker Objects
Images
An image is an immutable, layered filesystem template. It contains the application binaries, libraries, configuration files, and other files needed to run a workload.
Images are stored as content-addressed layers. When a new image reuses layers from an existing image, only the changed layers need to be downloaded. Read-only layers can also be shared safely between containers.
An image is not running merely because it exists on a host. It must be instantiated as a container.
Containers
A container is a running instance of an image. Docker adds a writable layer at the top of the image’s filesystem, allowing processes to make temporary changes without modifying the original image.
Containers also receive isolated views of system resources, such as processes, networking, and mount points. Resource limits can be applied through control groups.
For persistent data, Docker typically uses volumes rather than the container’s writable layer. Volumes are managed independently and can outlive the containers that use them.
Registries and Repositories
A registry is a service for storing and distributing images. Docker Hub, private registries, and cloud-hosted registry services all perform this role.
Within a registry, images are commonly organized into repositories and tagged with versions or identifiers, such as nginx:latest. A tag is a movable pointer to a particular image manifest, so production deployments should use immutable digests when strict version control is required.
What Happens During docker run?
The following command provides a useful example of the full execution path:
docker run -d -p 8080:80 nginx
The client sends an API request.
The Docker CLI parses the command and asksdockerdto create and start a container from thenginximage.The daemon resolves the image.
dockerdchecks whether the requested image is already available locally. If it is not, it contacts the registry, downloads the required layers, and verifies their content.The image is prepared.
The image manifest is stored, and its layers are unpacked into the snapshotter’s filesystem. Shared layers can remain in use by other images or containers.The daemon delegates container creation.
dockerdpasses a container creation task tocontainerd, including the image, networking settings, resource limits, and port mappings.The runtime prepares the execution environment.
containerdstarts the appropriate shim, which invokesruncwith an OCI runtime configuration.runcstarts the container process.runccreates namespaces, mounts the container filesystem, applies control-group restrictions, and starts thenginxprocess.The shim monitors the container.
Afterruncfinishes its setup and exits, the shim remains associated with the container process and reports its state back through the runtime.Port publishing is configured.
Docker arranges for traffic arriving on host port8080to reach container port80. The exact mechanism may involve userland proxying or host networking rules.The command returns.
Because-drequests detached mode, the CLI returns while the container continues running in the background.
Isolation and Resource Control
Docker containers rely on operating-system features to provide isolation.
Namespaces
Linux namespaces give each container restricted views of system resources. A container may see only its own processes, network interfaces, hostname, and mount points. Separate mount namespaces also prevent changes to one container’s filesystem from leaking into another.
Control groups
Control groups, or cgroups, limit and account for resource usage. Administrators can constrain CPU time, memory consumption, process counts, and I/O bandwidth for each container.
Namespaces provide visibility boundaries, while cgroups provide accounting and enforcement. Together, they form the foundation of Docker’s process and resource isolation.
Union Filesystems
Docker commonly uses a union filesystem such as OverlayFS to combine read-only image layers with a container’s writable layer. This is a copy-on-write design: files are copied into the writable layer only when they need to be modified.
This approach makes image distribution efficient and allows many containers to share the same underlying layers without duplicating disk space.
Why the Layered Design Matters
Docker’s architecture separates three major responsibilities:
- The client expresses intent.
- The daemon coordinates container operations.
- The runtime creates isolated processes.
This separation improves maintainability, reduces coupling, and makes it possible to replace or extend individual layers. It also allows platforms such as Kubernetes to interact with OCI-compatible runtimes without adopting the entire Docker command-line experience.
Understanding this request-to-execution path is essential for troubleshooting image pulls, container startup failures, networking problems, and resource-limit issues. It also provides the conceptual foundation for working with Docker networking, persistent storage, security, and orchestration.