Teaching Kubernetes About New Runtimes
By default, Kubernetes delegates container execution to whatever runtime is configured on the node — typically containerd using the runc handler. To use gVisor or Kata, you need a way to tell Kubernetes: "For this specific Pod, use a different execution handler."
The RuntimeClass resource is that mechanism. It is a cluster-scoped Kubernetes object (not namespaced) that maps a human-readable name to a specific runtime handler configured on the nodes.
The setup process has three distinct layers:
Layer 1 — Node-level installation: The runtime binary must be installed on the worker nodes. For gVisor, this means installing the runsc binary. For Kata, this means installing the Kata agent and its kernel image. This is infrastructure work, typically done via node provisioning scripts or a DaemonSet.
Layer 2 — Container engine configuration: The container engine (containerd or CRI-O) must be told that a new handler exists and where to find its binary. For containerd, this means editing /etc/containerd/config.toml to register the handler name and binary path.
For gVisor with containerd, the relevant section of config.toml registers a runsc runtime entry pointing at the io.containerd.runsc.v1 runtime type. This tells containerd: "When asked for a runtime named runsc, use the gVisor runtime type."
Layer 3 — Kubernetes RuntimeClass object: Finally, you create the Kubernetes API object that makes this handler visible to the scheduler and to Pod specs. It names the class (what developers will reference in Pod specs), the handler (which must exactly match the handler name registered in containerd's config), and, in heterogeneous clusters, a scheduling.nodeSelector restricting it to nodes that actually have the runtime installed and labeled accordingly.
The scheduling.nodeSelector field is critical in heterogeneous clusters. If only some nodes have gVisor installed, you must label those nodes and add the selector to the RuntimeClass. Kubernetes will then automatically apply this node selector to any Pod that requests this RuntimeClass — developers don't need to add it manually.
A common interview question is: "What happens if a Pod requests a RuntimeClass that doesn't exist, or if it's scheduled to a node that doesn't support it?" The answer: if the RuntimeClass object doesn't exist in the API, the Pod is rejected immediately at admission. If the RuntimeClass exists but the node doesn't have the handler installed, the Pod will be scheduled to the node but will fail at container creation time with a runtime error. The scheduling.nodeSelector field on the RuntimeClass prevents the second failure mode by ensuring the Pod only lands on capable nodes.