Skip to main content
In this lesson we explore Docker Engine architecture — how Docker runs applications inside isolated containers and what happens under the hood on a host running Docker. When people refer to the Docker Engine on a host, they usually mean a machine with Docker installed. On Linux, installing Docker provides three main components:
  • The Docker daemon (dockerd): a background service that manages Docker objects such as images, containers, volumes, and networks.
  • The Docker REST API: an HTTP API that clients (including the CLI) use to instruct the daemon. This API enables integration and automation with other tools.
  • The Docker CLI (docker): the command-line client that communicates with the daemon over the REST API.
The CLI does not need to run on the same machine as the daemon — you can point a local CLI at a remote Docker Engine using the -H/--host option. Example: run an nginx container on a remote Docker host over TLS:
Important: recent Docker Engine releases do not listen on plain TCP by default unless explicitly configured. The recommended remote access uses TLS on port 2376 or SSH. If you see examples using port 2375 without TLS, treat them as a security risk: 2375 exposes an unencrypted, unauthenticated Docker API that allows anyone who can reach it to control the host.
Port 2375 is the unencrypted, unauthenticated Docker API. Avoid it in production. Prefer 2376 with TLS or use SSH.
A safer alternative is to connect over SSH:

The runtime layers: dockerd, containerd, and runc

The Docker daemon does not perform the lowest-level container start operations itself. Starting and running containers involves two additional, focused components:
  • containerd: a general-purpose container runtime and supervisor. It pulls images from registries, manages container lifecycle (create, start, stop, delete), and supervises running containers. containerd is runtime-agnostic and widely used across the ecosystem.
  • runc: the low-level OCI runtime that invokes the kernel to set up namespaces and cgroups, then starts the container process. runc implements the Open Container Initiative (OCI) runtime spec for how containers run on Linux.
Sequence of handoffs when you run a container:
  • docker run ... → dockerd (Docker daemon)
  • dockerd → containerd (container runtime manager)
  • containerd → runc (OCI runtime) → Linux kernel (namespaces + cgroups)
Each layer has a clear responsibility:
  • dockerd: Docker-specific features (images, networks, volumes, registry auth, REST API).
  • containerd: image pulling, storage, lifecycle management, supervising containers.
  • runc: the final low-level setup with the kernel (namespaces, cgroups) and launching the container process.
This separation explains why Kubernetes removed Docker-specific shims without breaking container execution: Kubernetes talks directly to containerd (or other CRI-compliant runtimes) rather than depending on the Docker daemon. containerd has emerged as the de facto runtime across Docker, Kubernetes, and many managed platforms.
You rarely need to interact directly with containerd or runc for everyday Docker tasks. But knowing these layers helps when inspecting process lists, reading logs, or diagnosing runtime-level issues.

Summary table: components and responsibilities

Why this matters

Understanding these components clarifies troubleshooting steps (e.g., which logs to check), security considerations (exposed APIs vs. SSH/TLS), and how higher-level platforms like Kubernetes interface with runtimes.
A schematic titled "Underneath the daemon" showing the layered container stack: docker (CLI) → dockerd (Daemon) → containerd (Runtime) → runc (OCI) → Linux kernel (namespaces + cgroups). Side labels list responsibilities (images, networks, lifecycle, low-level launcher) and a Kubernetes icon indicates it talks directly to containerd.

Further reading

Watch Video