containerd is an open-source, industry-standard container runtime daemon that manages the complete container lifecycle on a host: it pulls and stores container images, prepares their file systems, starts and stops containers, and supervises their execution. In the world of containers, containerd is the layer that sits between high-level tools such as Docker or Kubernetes and the low-level runtime runc, which actually creates the isolated process.
containerd began as a component inside Docker and was donated to the Cloud Native Computing Foundation in 2017, where it later reached graduated status. Today it is the default runtime in most Kubernetes distributions and managed cloud Kubernetes services, and it still powers the Docker Engine underneath its familiar command-line interface.
How the containerd daemon runs containers
containerd runs as a long-lived system daemon that exposes a gRPC API, usually on the socket /run/containerd/containerd.sock. Clients such as the kubelet, Docker Engine or command-line tools send requests through this API, and containerd carries them out in a sequence of well-defined steps.
- Pull and store the image: containerd fetches the manifest and layers from a container registry and saves them in its content-addressed content store.
- Prepare the root file system: a snapshotter unpacks the layers and stacks them, typically with OverlayFS, into a writable view for the container.
- Create the runtime bundle: containerd generates an OCI runtime specification describing namespaces, cgroups, mounts and the command to run.
- Start a shim and runc: a small per-container shim process launches runc, which creates the isolated process and then exits.
- Supervise the container: the shim keeps standard I/O and exit status available, so containers survive a restart of containerd itself.
This design separates responsibilities cleanly. containerd handles images, storage and lifecycle, while the Open Container Initiative (OCI) runtime handles kernel-level isolation. Because the interface is standardized, runc can be replaced with alternatives such as crun, gVisor or Kata Containers without changing containerd.
Internal architecture and key concepts
containerd is built from pluggable services, each responsible for one part of the job. The table below lists the concepts that administrators encounter most often.
| Component | Function | Practical note |
|---|---|---|
| Content store | Holds image blobs addressed by digest | Identical layers are stored only once |
| Snapshotter | Builds container file systems from image layers | OverlayFS is the usual default on Linux |
| Metadata store | Tracks images, containers and leases in a local database | Drives garbage collection of unused data |
| Namespaces | Separate clients logically on one daemon | Kubernetes uses k8s.io, Docker uses moby |
| CRI plugin | Implements the Kubernetes Container Runtime Interface | Built in, no separate adapter needed |
| Runtime shim | Mediates between containerd and the OCI runtime | One shim per container or pod |
The containerd namespaces are a frequent source of confusion. They are not Linux kernel namespaces and not Kubernetes namespaces: they only partition containerd’s own objects. An image pulled by Docker therefore does not appear when Kubernetes tools query the k8s.io namespace on the same machine.
Leases and garbage collection keep storage under control. While a client is pulling or unpacking an image, containerd holds a lease that protects the partial data; once nothing references a blob or snapshot any more, the garbage collector removes it. Snapshotters can also be swapped: besides OverlayFS, containerd supports devmapper, ZFS and Btrfs snapshotters, and lazy-pulling snapshotters can start a container before every layer has finished downloading, which shortens cold-start time for large images.
containerd compared with related runtimes and tools
The container ecosystem has several layers of runtimes, and the names are easy to mix up. The comparison below shows where containerd fits.
| Tool | Layer | Main purpose |
|---|---|---|
| runc | Low-level OCI runtime | Creates one isolated process from a bundle |
| containerd | High-level runtime daemon | Images, storage, lifecycle and supervision |
| CRI-O | High-level runtime daemon | Runtime built only for Kubernetes |
| Docker Engine | Developer platform on top of containerd | Builds, networking, volumes and a friendly CLI |
| Podman | Daemonless container engine | Docker-compatible commands without a central daemon |
When Kubernetes removed the dockershim in version 1.24, clusters that used Docker as their runtime switched to containerd or CRI-O. Images built with Docker kept working, because they follow the OCI image format; only the runtime on the nodes changed.
Working with containerd in practice
containerd is usually operated through one of several client tools, chosen according to the task. Its main configuration lives in /etc/containerd/config.toml, where registries, snapshotters, runtime classes and the CRI plugin are defined.
| Tool | Audience | Typical use |
|---|---|---|
| ctr | containerd developers and debugging | Low-level image and container operations |
| crictl | Kubernetes node administrators | Inspect pods, containers and images via CRI |
| nerdctl | Users wanting Docker-like commands | Run, build and compose with familiar syntax |
| kubelet | Kubernetes nodes | Starts pods through the CRI plugin |
On Kubernetes nodes running systemd, the setting SystemdCgroup = true for the runc runtime is essential. It makes containerd and the kubelet use the same cgroup driver; a mismatch leads to unstable resource accounting and node problems under memory pressure.
Limitations and common pitfalls
containerd is deliberately minimal, which is a strength in production but surprises users who expect the full Docker experience. These are the most common issues.
- No built-in image building in containerd itself; builds use BuildKit, which nerdctl or Docker can drive.
- ctr is not a user-friendly CLI. It skips conveniences such as automatic registry login and port publishing.
- Disk usage grows silently when unused images accumulate; kubelet image garbage collection thresholds should be tuned.
- Registry mirror configuration changed format between major versions, so copied examples may not apply.
- Cgroup driver mismatches with the kubelet cause hard-to-diagnose node instability.
In everyday language
containerd is the engine room that actually runs containers on a computer. People and tools such as Docker or Kubernetes give the orders. containerd does the practical work behind the scenes.
Picture a busy port warehouse. Shipping companies send instructions, but the warehouse crew unloads the boxes, stores them and moves them to the right dock. containerd is that crew. It fetches software packages, stores them and starts them when asked.
Seen in the real world
A platform team runs a Kubernetes cluster of 30 nodes that still uses Docker Engine through the old dockershim. Before upgrading Kubernetes past version 1.23, the team must replace the runtime on every node.
They install containerd, enable the CRI plugin with the systemd cgroup driver, point the kubelet at the containerd socket and drain and convert nodes one at a time. Application images need no changes, because they are standard OCI images. Debugging habits change: engineers now use crictl ps and crictl logs instead of Docker commands. After the migration, each node runs one fewer daemon, and the cluster upgrade proceeds without application downtime.