containerd

On this page 7

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.

  1. Pull and store the image: containerd fetches the manifest and layers from a container registry and saves them in its content-addressed content store.
  2. Prepare the root file system: a snapshotter unpacks the layers and stacks them, typically with OverlayFS, into a writable view for the container.
  3. Create the runtime bundle: containerd generates an OCI runtime specification describing namespaces, cgroups, mounts and the command to run.
  4. Start a shim and runc: a small per-container shim process launches runc, which creates the isolated process and then exits.
  5. 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.

ComponentFunctionPractical note
Content storeHolds image blobs addressed by digestIdentical layers are stored only once
SnapshotterBuilds container file systems from image layersOverlayFS is the usual default on Linux
Metadata storeTracks images, containers and leases in a local databaseDrives garbage collection of unused data
NamespacesSeparate clients logically on one daemonKubernetes uses k8s.io, Docker uses moby
CRI pluginImplements the Kubernetes Container Runtime InterfaceBuilt in, no separate adapter needed
Runtime shimMediates between containerd and the OCI runtimeOne shim per container or pod
Main containerd components

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.

The container ecosystem has several layers of runtimes, and the names are easy to mix up. The comparison below shows where containerd fits.

ToolLayerMain purpose
runcLow-level OCI runtimeCreates one isolated process from a bundle
containerdHigh-level runtime daemonImages, storage, lifecycle and supervision
CRI-OHigh-level runtime daemonRuntime built only for Kubernetes
Docker EngineDeveloper platform on top of containerdBuilds, networking, volumes and a friendly CLI
PodmanDaemonless container engineDocker-compatible commands without a central daemon
containerd and neighboring tools

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.

ToolAudienceTypical use
ctrcontainerd developers and debuggingLow-level image and container operations
crictlKubernetes node administratorsInspect pods, containers and images via CRI
nerdctlUsers wanting Docker-like commandsRun, build and compose with familiar syntax
kubeletKubernetes nodesStarts pods through the CRI plugin
Client tools for containerd

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.