Sandboxed runtimes: gVisor & Kata
A kernel boundary for untrusted workloads.
Ordinary containers on a node all share one host kernel, so a kernel-level container escape does not land in another container — it lands on the node, with access to every other pod there. seccomp and AppArmor shrink the odds, but they are still filtering calls into the same shared kernel. When a workload is untrusted enough that you cannot accept that risk — multi-tenant platforms, customer-supplied code, anything you did not write — a sandboxed runtime gives that pod its own kernel boundary.
Two mainstream options. gVisor runs a user-space kernel (runsc) that intercepts a container’s syscalls and services most of them itself, so very few ever reach the host kernel. Kata Containers goes further and wraps each pod in a lightweight virtual machine with a real, separate kernel. You expose either through a RuntimeClass and opt a pod in with runtimeClassName — everything else about the pod spec is unchanged.
apiVersion: node.k8s.io/v1kind: RuntimeClassmetadata: { name: gvisor }handler: runsc # the gVisor handler, installed + configured on nodes---apiVersion: v1kind: Podmetadata: { name: untrusted, namespace: sandbox }spec:runtimeClassName: gvisorcontainers:- { name: app, image: registry.internal/untrusted:1.0 }
The tradeoff
Isolation is not free: both runtimes add latency and some syscall or feature incompatibility (gVisor implements most but not all of the Linux surface; Kata carries VM overhead). So you apply them selectively — the risky tenant, the untrusted job — often on a dedicated node pool the RuntimeClass schedules onto, rather than cluster-wide. You can confirm a pod really landed in the sandbox from inside it.
# inside a gVisor pod, the kernel identifies itself as gVisor$ kubectl exec -n sandbox untrusted -- dmesg 2>/dev/null | grep -i gvisor... Starting gVisor...$ kubectl exec -n sandbox untrusted -- uname -aLinux untrusted 4.4.0 ... gVisor