containerd-shim

Rust framework for building containerd runtime v2 shims with TTRPC, logging, and event plumbing built in

Framework
Cargo
v0.11.0
241stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
74/100Good
Development Activity72
Maintenance68
Community80
Maturity56
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
71/100Good
Architecture82
Code Quality74
Innovation70
Learning Curve58

containerd-shim is the Rust crate that implements containerd’s runtime v2 shim protocol, letting you write a custom container runtime shim without hand-rolling the TTRPC server, command-line parsing, process reaping, or event publishing that containerd expects every shim to provide. It mirrors the API shape of containerd’s own Go shim.Run helper, so teams porting or extending runtime integrations (gVisor, Kata, Firecracker-based runtimes, WASM runtimes, and similar) get a familiar Shim and Task trait pair to implement instead of reverse-engineering the v2 shim wire protocol from the Go source.

It ships both a synchronous and an async (Tokio-based) implementation gated behind the async feature flag, plus first-class Windows and Linux support with platform-specific mounting, cgroup, and process-reaping code paths. The crate is maintained under the official containerd GitHub organization as part of the rust-extensions workspace alongside the sibling containerd-shim-protos, containerd-client, and runc crates.

What You Get

  • A Shim trait (new, start_shim, delete_shim, wait, create_task_service) that maps directly onto containerd’s Go shim.Run lifecycle, in both sync and async (Tokio) flavors
  • A Task trait pre-wired to the generated TTRPC service definitions from containerd-shim-protos, covering the full runtime v2 task surface (create, start, delete, exec, pids, stats, connect, shutdown, and more)
  • Built-in shim::run::<Service>() bootstrap that handles CLI flag parsing (Flags), logger setup with FIFO redirection, and socket address negotiation with the containerd daemon
  • Automatic child-process reaping and subreaper configuration on Unix so shim implementations don’t leak zombie processes
  • Cross-platform mount helpers (mount_linux / mount_other) and a Linux cgroup module for runtime implementations that need to manage container resource limits directly
  • An ExitSignal and RemotePublisher for signaling shutdown and publishing containerd events (task-exit, task-oom, etc.) over the shim’s TTRPC event channel

Common Use Cases

  • Implementing a runtime v2 shim for a non-runc sandbox technology (gVisor, Kata Containers, Firecracker microVMs, WASM/WASI runtimes) that needs to speak containerd’s shim protocol
  • Building an internal or experimental container runtime and needing a conformant containerd integration point without reimplementing the TTRPC wire protocol
  • Extending or forking containerd-runc-shim (the reference runc-based shim in the same workspace) to add custom lifecycle hooks, logging, or event handling
  • Cross-compiling a shim binary for Windows hosts using the crate’s windows-sys-backed pipe and console handling instead of writing platform-specific glue by hand

Under The Hood

Architecture: The crate centers on two traits — Shim (process/lifecycle: new, start_shim, delete_shim, wait, create_task_service) and Task (the TTRPC service handlers for create/start/delete/exec/etc., generated from containerd-shim-protos). shim::run::<Service>() in lib.rs is the single entry point: it parses Flags via args.rs, sets up logging (logger.rs) redirected through a FIFO back to containerd, spawns the TTRPC server, wires monitor.rs/reap.rs for child-process reaping and subreaping, and dispatches to whichever implementation (synchronous/ or asynchronous/, chosen at compile time via the async feature and mirrored mod.rs/monitor.rs/publisher.rs/util.rs file pairs) the shim was built with. Platform divergence (mount_linux.rs vs mount_other.rs, cgroup.rs Linux-only) is handled with #[cfg(target_os = ...)] gates rather than runtime branching, keeping the hot path free of platform checks.

Tech Stack: Pure Rust, edition 2021, part of a Cargo workspace with containerd-shim-protos (TTRPC/protobuf bindings) as its core dependency. TTRPC transport comes from the ttrpc crate re-exported through protos; async mode layers in tokio (multi-thread runtime, process/signal/fs features), async-trait, and futures; sync mode has no async runtime dependency at all. Process/OS integration uses nix (fs, socket, signal, mount, sched features) on Unix and windows-sys + mio on Windows; oci-spec provides OCI runtime spec types, cgroups-rs handles Linux cgroup management, and thiserror backs a single unified Error enum with #[from] conversions from ttrpc, protobuf, io, nix, and env-var errors.

Code Quality: Unit tests are present across roughly a third of the source modules (9 of ~13 top-level files carry #[test], 13 carry #[cfg(test)] blocks), covering flag parsing, error conversion paths, and util helpers, though there’s no dedicated integration test suite for full shim lifecycles in this crate (that’s exercised indirectly via containerd-runc-shim). Error handling is centralized and idiomatic — a single thiserror-derived Error enum with explicit variants (InvalidArgument, Ttrpc, Protobuf, IoError { context, err }, NotFoundError, etc.) rather than boxed dynamic errors, giving callers precise match targets. Naming and module boundaries closely track the Go containerd/runtime/v2 source it mirrors, which keeps the crate easy to cross-reference against upstream Go shim implementations.

API Design: The public surface is deliberately small and symmetric between sync and async — implement Shim + Task, call shim::run::<Service>(id, None), done. The README’s own “Look and feel” section shows the async example is nearly line-for-line the same shape as the sync one, which keeps the migration cost low when a runtime needs to move from blocking to async I/O. The tradeoff is upfront context cost: consumers need to understand containerd’s runtime v2 protocol (TTRPC, the task lifecycle state machine, event publishing) before the trait methods make sense, so the crate is best approached with the linked Go reference implementation open alongside it rather than from the Rust docs in isolation.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search