Open-Source Wikis

/

containerd

/

containerd

/

Architecture

containerd/containerd

Architecture

containerd runs as a daemon process that loads a graph of plugins and exposes their services over gRPC and ttrpc sockets. Containers themselves are not run inside the daemon; each container is launched as a separate shim process which talks to the daemon over ttrpc and to the OCI runtime (runc on Linux, runhcs on Windows) on the other side.

Process layout

graph LR
    Client[ctr / nerdctl / Docker / kubelet]
    Daemon[containerd daemon]
    Shim[containerd-shim-runc-v2]
    Runc[runc]
    Container((container))

    Client -->|gRPC / CRI| Daemon
    Daemon -->|spawn + ttrpc| Shim
    Shim -->|exec| Runc
    Runc --> Container
    Shim -->|events| Daemon
    Daemon -->|events| Client
  • The daemon (cmd/containerd/main.go) is a small bootstrap that calls into cmd/containerd/command to build the urfave/cli app, then cmd/containerd/server to construct a Server from the loaded plugins.
  • A shim is launched per container (or per pod sandbox) by the runtime plugin. The shim outlives the daemon — if containerd is restarted, running containers keep running and the shim re-attaches.
  • Clients can be the in-process Go client at client/, the ctr CLI, the kubelet via the CRI plugin, or any third-party gRPC client.

See containerd binary, containerd-shim-runc-v2, and runtime v2 system for the per-binary details.

Plugin model

Almost everything inside the daemon is a plugin. At startup, LoadPlugins in cmd/containerd/server/server.go asks the global plugin registry for the topologically-sorted graph of registered plugins, applies disabled-plugin filters from the config, then initializes each plugin in dependency order.

Each plugin is identified by a (Type, ID) pair where the type is one of the constants in plugins/types.go:

Plugin type URI Examples
RuntimePluginV2 io.containerd.runtime.v2 runc shim launcher in core/runtime/v2
SnapshotPlugin io.containerd.snapshotter.v1 overlayfs, btrfs, devmapper, native, blockfile, erofs, windows, lcow
ContentPlugin io.containerd.content.v1 local content store at plugins/content/local
MetadataPlugin io.containerd.metadata.v1 bbolt-backed metadata store at plugins/metadata
DiffPlugin io.containerd.differ.v1 walking, erofs, windows, lcow differs in plugins/diff/*
GRPCPlugin io.containerd.grpc.v1 each gRPC service registered under plugins/services/*
TTRPCPlugin io.containerd.ttrpc.v1 ttrpc shim entry points
ServerPlugin io.containerd.server.v1 plugins/server/grpc, plugins/server/ttrpc, plugins/server/debug, plugins/server/metrics
SandboxControllerPlugin io.containerd.sandbox.controller.v1 plugins/sandbox, internal/cri/server/podsandbox
CRIServicePlugin io.containerd.cri.v1 CRI runtime/image services in internal/cri
NRIApiPlugin io.containerd.nri.v1 Node Resource Interface adapter in plugins/nri
TransferPlugin io.containerd.transfer.v1 image pull/push transfer service in plugins/transfer
EventPlugin io.containerd.event.v1 event exchange in plugins/events
GCPlugin io.containerd.gc.v1 garbage collection scheduler in plugins/gc
LeasePlugin io.containerd.lease.v1 lease manager in plugins/leases
StreamingPlugin io.containerd.streaming.v1 streaming manager in plugins/streaming
WarningPlugin io.containerd.warning.v1 deprecation/warning service
MountManagerPlugin / MountHandlerPlugin io.containerd.mount-manager.v1 / io.containerd.mount-handler.v1 plugins/mount, plugins/mount/erofs, plugins/mount/fsview
ImageVerifierPlugin io.containerd.image-verifier.v1 binary image verifier in plugins/imageverifier

Built-ins are wired into the daemon by blank-importing them from cmd/containerd/builtins/builtins.go. Proxy plugins are configured by the operator in the daemon TOML and connect to an out-of-process gRPC server speaking the corresponding service API; see proxy plugins.

Request flow: pulling and running an image

sequenceDiagram
    autonumber
    participant Client
    participant Daemon as containerd daemon
    participant Transfer as Transfer service
    participant Content as Content store
    participant Meta as Metadata (bbolt)
    participant Snap as Snapshotter
    participant Runtime as Runtime v2
    participant Shim
    participant Runc

    Client->>Daemon: Transfer(Pull{ref})
    Daemon->>Transfer: stream pull
    Transfer->>Content: write blobs
    Transfer->>Meta: record image manifest
    Client->>Daemon: NewContainer(image, snapshotter)
    Daemon->>Snap: Prepare(rootfs)
    Daemon->>Meta: store container record
    Client->>Daemon: Tasks.Create(container)
    Daemon->>Runtime: Create(opts, mounts)
    Runtime->>Shim: spawn binary
    Shim->>Runc: create + start
    Runc-->>Shim: container running
    Shim-->>Daemon: TaskCreate / TaskStart events
    Daemon-->>Client: events stream

The same flow is driven by the kubelet through the CRI plugin in internal/cri, but the container creation is wrapped inside a sandbox.

Storage layout on disk

containerd separates persistent state from volatile state:

  • --root (default /var/lib/containerd) holds long-lived data: the bbolt metadata DB, the local content store, snapshotter data, etc. Each plugin gets its own subdirectory (io.containerd.<type>.<version>.<id>/).
  • --state (default /run/containerd) holds runtime data that should disappear on reboot: shim sockets, fifos, task state.

Both paths are created by CreateTopLevelDirectories in cmd/containerd/server/server.go with mode 0700 (root) and 0711 (state).

Sockets

By default the daemon listens on:

  • unix:///run/containerd/containerd.sock — gRPC API for clients
  • unix:///run/containerd/containerd.sock.ttrpc — ttrpc API for shims
  • unix:///run/containerd/debug.sock — debug/introspection (when enabled)

The defaults/ package centralizes these constants per-OS.

Cross-cutting concerns

  • Namespaces isolate containers/images/leases on the same daemon. Every gRPC call carries a namespace via metadata; see pkg/namespaces and namespaces glossary.
  • Leases prevent garbage collection of in-progress objects (a downloaded blob, an active snapshot). See plugins/leases and core/leases.
  • Garbage collection runs in plugins/gc, scheduled against the metadata store; objects without a lease and without inbound references are deleted.
  • Tracing is wired through OpenTelemetry. The default tracing processor lives in plugins/server/internal and the SDK is configured by tracing.go builtins.
  • Metrics are Prometheus-compatible, exposed by plugins/server/metrics.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Architecture – containerd wiki | Factory