Open-Source Wikis

/

containerd

/

Apps

/

containerd

containerd/containerd

containerd

The main daemon. A small main (cmd/containerd/main.go) hands off to cmd/containerd/command which builds an urfave/cli app, then cmd/containerd/server constructs the actual server from the loaded plugin graph.

Purpose

containerd is a long-running process that:

  1. Reads a TOML config (/etc/containerd/config.toml by default).
  2. Builds a directed graph of plugins (built-ins plus proxy plugins from config).
  3. Initializes plugins in dependency order and starts any ServerPlugins.
  4. Listens on gRPC, ttrpc, debug, and metrics sockets and serves API requests until SIGTERM.

Directory layout

cmd/containerd/
├── main.go               # tiny entrypoint
├── builtins/             # blank imports that pull in every built-in plugin
│   ├── builtins.go       # cross-platform builtins
│   ├── builtins_linux.go # Linux-only (devmapper, btrfs, zfs, blockio, NRI)
│   ├── builtins_windows.go
│   ├── builtins_freebsd.go
│   ├── builtins_unix.go
│   ├── btrfs_linux.go    # btrfs snapshotter (build-tag gated)
│   ├── devmapper_linux.go
│   ├── zfs_linux.go
│   ├── cri.go            # CRI plugin (gated by no_cri)
│   └── tracing.go        # OTel tracing pipeline
├── command/              # urfave/cli setup, version/config/publish subcommands
└── server/               # daemon construction and lifecycle
    ├── config/           # TOML config schema
    ├── server.go         # New(), LoadPlugins(), Start(), Stop()
    └── server_linux.go   # Linux-specific bits (rootless setup, etc.)

Key abstractions

Type / function File Purpose
command.App() cmd/containerd/command/main.go Build the urfave/cli command tree
server.New(ctx, *Config) cmd/containerd/server/server.go Apply config, load plugins, return a Server
server.LoadPlugins cmd/containerd/server/server.go Register proxy plugins, ask registry.Graph for the topological order
server.CreateTopLevelDirectories cmd/containerd/server/server.go mkdir --root (0700) and --state (0711)
server.Server.Start cmd/containerd/server/server.go Call Start on every loaded ServerPlugin
server.Server.Stop cmd/containerd/server/server.go Reverse-close every plugin that's an io.Closer
server.Server.RegisterReadiness / Wait cmd/containerd/server/server.go Two-phase startup so plugins can signal "I'm ready"

How startup works

graph TD
    main[main.go]
    cli[command.App]
    config[load *.toml]
    builtins[blank imports register plugins]
    new[server.New]
    loadp[LoadPlugins → registry.Graph]
    init[for each plugin: Init]
    start[server.Start → plugins/server/*]
    sock[listen on grpc / ttrpc / debug / metrics]

    main --> cli
    cli --> config
    builtins -.->|init() in registry| new
    config --> new
    new --> loadp
    loadp --> init
    init --> start
    start --> sock
  1. main() (in cmd/containerd/main.go) is six lines: build the cli app and run it.
  2. command.App registers subcommands: the default run, plus config, publish, version, and oci-hook.
  3. The default action loads the TOML config (server/config), creates the root/state directories, and calls server.New.
  4. server.New registers any proxy plugins declared in [proxy_plugins.*] by translating their type to a plugins.Type constant and registering a plugin.Registration whose InitFn dials the configured socket.
  5. LoadPlugins then asks github.com/containerd/plugin/registry.Graph for the dependency-ordered list, applies the disabled-plugin filter (config.DisabledPlugins), and returns the graph.
  6. server.New walks the graph, building a plugin.InitContext for each entry that has the plugin's root/state directories and the gRPC/ttrpc addresses. result.Instance() triggers the plugin's actual InitFn.
  7. Plugins of type ServerPlugin are appended to the daemon's servers slice. After the graph is fully initialized, Server.Start is called, which calls Start(ctx) on each one — that's how the gRPC, ttrpc, debug, and metrics listeners are bound.
  8. The CLI then blocks until a signal arrives, at which point Server.Stop walks the plugin list in reverse and Close()s anything that implements io.Closer.

Sockets the daemon binds

All addresses are configurable via config.toml. Defaults from defaults/:

Listener Default address Plugin
gRPC API unix:///run/containerd/containerd.sock plugins/server/grpc
ttrpc API (shims) unix:///run/containerd/containerd.sock.ttrpc plugins/server/ttrpc
Debug (pprof, introspection helpers) unix:///run/containerd/debug.sock plugins/server/debug
Metrics (Prometheus) tcp://127.0.0.1:1338 (when configured) plugins/server/metrics

Config

The TOML schema is defined in cmd/containerd/server/config/config.go. Top-level fields include Root, State, TempDir, Version, RequiredPlugins, DisabledPlugins, OOMScore, Timeouts, StreamProcessors, ProxyPlugins, and the per-plugin Plugins map.

containerd config default and containerd config dump (in cmd/containerd/command/config.go) print the default and effective configs respectively. The diff between them is the user's customization.

Entry points for modification

  • Adding a new built-in plugin: register an init() and add a blank import to cmd/containerd/builtins/builtins.go (or a platform-specific sibling).
  • Adding a new daemon subcommand: extend cmd/containerd/command/.
  • Changing startup ordering: examine the Requires field on plugin registrations — that's what registry.Graph topologically sorts on.
  • Changing the config schema: edit cmd/containerd/server/config/. Be mindful of backward compatibility (V2DisabledFilter, Decode per-plugin migrations).

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

containerd – containerd wiki | Factory