The containerd shim process is the unsung hero of container lifecycle management, acting as a crucial intermediary between containerd itself and the actual container processes, but its real magic lies in its ability to outlive its parent.

Let’s see it in action. Imagine we have a simple busybox container running:

# First, get the container ID
ctr-remote i o --quiet type=container label=containerd.io/gc.root=containerd \
    | grep busybox \
    | awk '{print $1}'

# Let's say the ID is 'a1b2c3d4e5f6'
CONTAINER_ID="a1b2c3d4e5f6"

# Now, let's look at the processes associated with it
# The 'c' flag shows the command, 'p' shows the PID
ctr-remote i o --quiet type=task id=$CONTAINER_ID \
    | awk '{print $1}' \
    | xargs -I {} ctr-remote t o --quiet id={} \
    | grep $CONTAINER_ID

# This will output something like:
# task_id: a1b2c3d4e5f6 pid: 12345 command: /usr/bin/containerd-shim-runc-v2 --address /run/containerd/io.containerd.runtime.v2.task/default/a1b2c3d4e5f6/shim.sock --debug --state /run/containerd/runc/default/a1b2c3d4e5f6 --io-fd 3 --exit-fd 4

Here, containerd-shim-runc-v2 is the shim process. Notice its PID (e.g., 12345) and its arguments, particularly the --address pointing to a Unix domain socket. This socket is how containerd communicates with the shim. The --state directory holds crucial information about the container’s runtime state.

The problem the shim solves is straightforward: when containerd launches a container, it needs a way to track its lifecycle (start, stop, signal, etc.) even if containerd itself restarts. If containerd went down and then came back up, it would lose track of all its running containers. The shim process, however, is designed to persist.

Here’s how it works internally:

  1. Containerd Initiates: When you tell containerd to run a container (e.g., via ctr run), containerd doesn’t directly execute the container’s entrypoint. Instead, it launches a containerd-shim process.
  2. Shim Forks the Runtime: The shim process is responsible for interacting with the actual container runtime (like runc). It uses the runtime to create and start the container. Critically, the shim forks the container process.
  3. Parent/Child Relationship: The shim process becomes the direct parent of the container’s init process. This is a standard Unix pattern: the parent process monitors its child.
  4. Containerd Detaches: Once the shim has successfully launched the container and established communication (via the Unix domain socket), containerd considers its job done for the initial launch. It can then detach from the shim. If containerd were to crash and restart, it would look at its state files (or re-establish communication with running shims) and realize these shims are still running, thus keeping the containers alive.
  5. Shim Survives Containerd: The shim process continues to run, even if containerd restarts. It acts as a proxy, relaying signals from containerd (or other management tools) to the container and forwarding exit status back to containerd.
  6. Container Exit: When the container process exits, the shim detects this. It then exits itself, and because it was the parent of the container’s init process, it cleanly cleans up any remaining resources. Containd is notified of the shim’s exit and marks the container as exited.

The key lever you control is the runtime specified when you configure containerd or when you launch a container. The shim process’s name reflects this: containerd-shim-runc-v2 means it’s using the runc runtime with version 2 of its interface. Other runtimes (like crun) would have different shim names. The --address socket is the primary communication channel.

When a container’s PID 1 exits, the shim process immediately exits. This is by design; the shim’s sole purpose is to manage the lifecycle of the container’s init process. If that init process is gone, the shim’s job is done.

The next concept you’ll likely encounter is how containerd uses namespaces to isolate these shims and their associated containers, and the role of the containerd daemon in managing the overall container orchestration.

Want structured learning?

Take the full Containerd course →