Containerd Explained: The Lean Container Runtime Powering Kubernetes (Not an Orchestrator)

If you’re building on Kubernetes or modern CI/CD, you’re already touching containerd—even if you never type its name. Teams often mistake it for “another Docker” or even an orchestrator. It’s neither. containerd is a high-performance, OCI-compliant container runtime that sits under your tools, doing the heavy lifting: fetching images, managing snapshots, starting/stopping containers, and exposing clean APIs that higher layers (like Kubernetes) use.


What containerd is (and isn’t)

  • Is: A CNCF-graduated, production-grade container runtime with a stable API and gRPC services for images, snapshots, tasks, events.
  • Is not: A scheduler or orchestrator. Kubernetes, Nomad, or Swarm do the orchestration; containerd runs the containers.
  • Works with:
    • Kubernetes via CRI (kubelet ↔ CRI plugin ↔ containerd ↔ runc)
    • nerdctl (a Docker-like CLI for containerd)
    • runc as the default OCI runtime (pluggable)

Architecture at a glance

Think of a restaurant:

  • Kubernetes kubelet = maître d’ taking orders (Pods)
  • containerd = kitchen manager coordinating stations (images, snapshots, tasks)
  • runc = the cook that actually executes the recipe (creates Linux processes in namespaces/cgroups)
  • Snapshotters (overlayfs, btrfs, zfs) = pantry systems handling copy-on-write layers
  • Content store = walk-in fridge for OCI artifacts (images, layers)

Core components:

  • Content: Stores blobs (layers, configs) with CAS semantics.
  • Images: References to content with metadata.
  • Snapshotters: Filesystem layer mounts (overlayfs is most common).
  • Tasks: Running containers (via runc or another OCI runtime).
  • CRIs: Kubernetes integration (containerd’s CRI plugin).
  • Plugins: Extensible points for runtime, snapshotters, content, metrics.

When to choose containerd

  • You want Kubernetes without the Docker shim (native CRI, fewer moving parts).
  • You need performance and predictable behavior for large clusters.
  • You prefer a minimalist, composable runtime you can script and observe.
  • You’re building edge or appliance workloads with tight resource budgets.

Quick comparison

Capability / ToolcontainerdDocker EngineCRI-OruncPodman
RoleRuntime (API + services)Engine (CLI + API + runtime)Runtime (K8s-focused)Low-level OCI executorCLI/daemonless engine
K8s CRIYes (built-in)Deprecated for K8sYesNoVia crun/other runtimes + CRI-O
CLI includedNo (use ctr/nerdctl)Yes (docker)NoNoYes (podman)
ScopeLean, composableFull developer UXK8s-only minimalismStart/kill containersDev + rootless focus
Typical useProduction under K8sDev + legacy prodStrict K8s shopsUsed by containerdDev/ops alt to Docker

Hands-on: containerd basics

Install & sanity check

# systemd-based
sudo systemctl enable --now containerd
ctr version

Pull and run with ctr (low-level)

sudo ctr images pull docker.io/library/nginx:1.27
sudo ctr run --rm --net-host docker.io/library/nginx:1.27 web1

Use nerdctl (Docker-like UX)

# great for day-to-day
sudo nerdctl run -d --name web -p 8080:80 nginx:1.27
sudo nerdctl ps
sudo nerdctl logs -f web

Configure containerd (snippet)

/etc/containerd/config.toml

version = 2

[plugins.”io.containerd.grpc.v1.cri”.containerd]

snapshotter = “overlayfs” disable_snapshot_annotations = false

[plugins.”io.containerd.grpc.v1.cri”.registry.mirrors.”docker.io”]

endpoint = [“https://registry-1.docker.io”]

sudo systemctl restart containerd

Kubernetes with containerd (concept)

  • kubelet talks CRI → containerd’s CRI plugin → containerd spawns tasks via runc.
  • No Docker shim, fewer layers to debug.

Observability & debugging

  • List images/containers: ctr images ls, ctr tasks ls, nerdctl ps
  • Events stream: ctr events
  • Runtime state: sudo crictl ps -a (if CRI tools installed)
  • Logs: journalctl -u containerd -f
  • Inspect image content: ctr content ls, ctr images info <ref>

Best practices

  • Prefer nerdctl for everyday UX; fall back to ctr for internals.
  • Pin base images (e.g., nginx:1.27.2) to avoid surprise upgrades.
  • Harden the node: enable AppArmor/SELinux; keep runc/containerd patched.
  • Choose the right snapshotter:
    • overlayfs: default, fast on most distros
    • btrfs/zfs: advanced features, but operational trade-offs
  • Tune cgroups for noisy neighbors (CPU quotas, memory limits).
  • Use private registries with auth & content trust where possible.
  • Rootless where appropriate (nerdctl + rootless containerd) for security.

Common pitfalls (and fixes)

  • “containerd isn’t an orchestrator.” True—pair it with Kubernetes or Nomad to schedule.
  • Image GC surprises: Layers reclaimed if unreferenced. Set sensible GC thresholds and keep pull-through caches warm.
  • DNS/network quirks in Pods: Usually a CNI plugin issue, not containerd. Check CNI config and kubelet resolv.conf options.
  • Overlayfs on exotic filesystems: Validate kernel support and xattr options; consider alternate snapshotters if needed.
  • Mixed CLIs confusion: Don’t mix docker and nerdctl on the same node for the same workloads; state lives in different runtimes.

Real-world pattern: high-throughput API on K8s

  1. Kubernetes schedules 1,000+ Pods across nodes.
  2. kubelet on each node asks containerd (via CRI) to create Pods/containers.
  3. containerd pulls images once per node, mounts layers via overlayfs, and launches tasks via runc.
  4. DaemonSets push node-exporter and containerd metrics to Prometheus.
  5. Rolling updates: new image → containerd streams layers with content-store dedupe → fast rollout with minimal bandwidth.

Impact: faster start times, fewer moving parts, clean debugging (kubelet → CRI → containerd → runc).


Security checklist

  • Keep containerd, runc, and the kernel up to date (watch CVEs).
  • Enforce seccomp/AppArmor/SELinux profiles.
  • Use read-only root filesystems and non-root users in images.
  • Restrict privileged and hostPath usage in K8s.
  • Sign/verify images (cosign), and enable image policy in admission control.

Internal link ideas (for your doc site/blog)

  • “Kubernetes CRI Deep Dive: kubelet ↔ containerd”
  • “OCI Artifacts & Content Store 101”
  • “Choosing a Snapshotter: overlayfs vs btrfs vs zfs”
  • “Rootless Containers with nerdctl”
  • “runc vs crun: Performance and Security”

Conclusion & takeaways

  • containerd is the runtime backbone, not the scheduler.
  • It delivers speed, simplicity, and reliability at scale.
  • Pair it with Kubernetes, use nerdctl for dev ergonomics, harden the node, and monitor the runtime.
  • Get the layers right (content, snapshots, tasks), and your platform stays boring—in a good way.

Image prompt (for AI tools)

“A clean, modern diagram of the container runtime stack showing kubelet → CRI → containerd (content, images, snapshots, tasks) → runc → Linux namespaces/cgroups; include registry and snapshotter; minimalistic, high-contrast, 3D isometric style.”


Tags

#Containerd #Kubernetes #OCI #DevOps #CloudNative #Containers #Runtime #Linux
Containerd, Kubernetes, OCI, DevOps, Cloud Native, Containers, Runtime, Linux