Introduction
The Docker Engine refers to the server-side components of Docker responsible for building, sharing, and running containers. Rather than being a single monolithic program, it is a modular system composed of many small, specialized tools contributed by projects such as the OCI, the CNCF, and the Moby Project.
From Monolith to Modular Design
When Docker was first released, the Engine consisted of two major parts: the Docker daemon, a monolithic binary handling the API, image building, and container execution, and LXC, which interfaced directly with the Linux kernel.
Relying on LXC created problems, since it was Linux-specific and evolved independently of Docker's needs. Docker eventually replaced it with its own platform-agnostic tool called libcontainer.
Over time, the monolithic daemon itself became a bottleneck for innovation, so Docker gradually broke it apart into smaller, reusable tools. The most notable results of this effort were containerd and runc, which are now used far beyond Docker itself, including in Kubernetes, Firecracker, and Fargate.
The Influence of the OCI
Around the same time, the Open Container Initiative (OCI) was defining two key standards: the image-spec and the runtime-spec, both released as version 1.0 in 2017. A third standard, the distribution-spec, governs how images move through registries.
Docker, Inc. was a founding member of the OCI and remains actively involved. Every modern version of Docker implements these specifications through tools like runc, BuildKit, and Docker Hub.
runc: The Low-Level Runtime
runc is the reference implementation of the OCI runtime-spec. It is a lightweight CLI wrapper around libcontainer, responsible for interfacing with the kernel to actually build and start a container's namespaces and cgroups. Once a container starts, the runc process that created it exits.
containerd: The High-Level Runtime
containerd manages the lifecycle of containers — starting, stopping, and deleting them — while delegating the low-level kernel work to runc. Originally intended as a small, focused tool, it has since grown to also manage images, networks, and volumes, which projects like Kubernetes can selectively use.
containerd was created by Docker, Inc. and later donated to the CNCF, where it is now a graduated, production-ready project.
How a Container Gets Started
When a command such as the following is run:
docker run -d --name ctr1 nginxthe CLI converts it into an API request sent to the daemon. The daemon passes the request to containerd over gRPC, which converts the image into an OCI bundle and instructs runc to create the container. runc interfaces with the kernel to build the required namespaces and cgroups, then exits once the container process is running.
This separation means the daemon can be stopped, restarted, or updated without affecting already-running containers — a concept known as daemonless containers.
What Is a Shim?
Between containerd and the OCI layer sits a shim process. For every new container, containerd forks a shim and a runc process; once runc exits, the shim becomes the container's parent process. Shims provide three key benefits: enabling daemonless containers, improving efficiency by keeping processes lightweight, and allowing runc to be swapped for other low-level runtimes.
Linux Implementation
On a Linux host, these components exist as separate binaries:
/usr/bin/dockerd/usr/bin/containerd/usr/bin/containerd-shim-runc-v2/usr/bin/runc
Conclusion
The Docker Engine's modular design — built around the OCI specifications, containerd's lifecycle management, and runc's low-level execution — has made it more flexible, portable, and reusable across the wider container ecosystem. Understanding these layers is key to truly mastering how Docker works under the hood.