Kubernetes gVisor and Kata Containers RuntimeClass
Deploy sandboxed container runtimes on Kubernetes using RuntimeClass with gVisor (runsc) and Kata Containers. Isolate untrusted workloads with kernel-level
π‘ Quick Answer: RuntimeClass lets you run specific pods with sandboxed runtimes instead of the default runc. gVisor (runsc) interposes a user-space kernel between the container and host β no direct syscalls. Kata Containers runs each pod in a lightweight VM. Create a RuntimeClass, configure containerd with the handler, then set
runtimeClassNamein your pod spec.
The Problem
- Default runc containers share the host kernel β a kernel exploit escapes to host
- Multi-tenant clusters need stronger isolation than Linux namespaces/cgroups
- Untrusted code (CI builds, user uploads, AI inference) needs sandboxing
- Compliance requires defense-in-depth beyond standard container boundaries
- Need different isolation levels for different workloads in same cluster
The Solution
RuntimeClass Definition
# gVisor RuntimeClass
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc # Must match containerd config handler name
scheduling:
nodeSelector:
sandbox.gvisor.dev/runtime: "true"
---
# Kata Containers RuntimeClass
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata
handler: kata # Must match containerd config handler name
scheduling:
nodeSelector:
katacontainers.io/kata-runtime: "true"
overhead:
podFixed:
memory: "160Mi" # VM overhead
cpu: "250m"Configure containerd for gVisor
# /etc/containerd/config.toml β add gVisor handler
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc.options]
TypeUrl = "io.containerd.runsc.v1.options"
ConfigPath = "/etc/containerd/runsc.toml"# Install gVisor
curl -fsSL https://gvisor.dev/archive.key | gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" > /etc/apt/sources.list.d/gvisor.list
apt-get update && apt-get install -y runsc
# Verify
runsc --version
systemctl restart containerdConfigure containerd for Kata
# /etc/containerd/config.toml β add Kata handler
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata]
runtime_type = "io.containerd.kata.v2"
privileged_without_host_devices = true
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.kata.options]
ConfigPath = "/opt/kata/share/defaults/kata-containers/configuration.toml"Use RuntimeClass in Pods
# Run untrusted workload in gVisor sandbox
apiVersion: v1
kind: Pod
metadata:
name: untrusted-build
spec:
runtimeClassName: gvisor # β This is all you need
containers:
- name: build
image: registry.example.com/ci-runner:v1
command: ["./run-untrusted-build.sh"]
resources:
limits:
cpu: "2"
memory: "4Gi"
---
# Run in Kata VM-level isolation
apiVersion: v1
kind: Pod
metadata:
name: isolated-inference
spec:
runtimeClassName: kata
containers:
- name: inference
image: registry.example.com/model-server:v1
resources:
limits:
cpu: "4"
memory: "8Gi"Comparison: runc vs gVisor vs Kata
Feature β runc (default) β gVisor (runsc) β Kata Containers
ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββΌβββββββββββββββββ
Isolation β Namespaces β User-space β VM (hypervisor)
β + cgroups β kernel β
ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββΌβββββββββββββββββ
Kernel access β Shared host β Intercepted β Separate guest
β kernel β (Sentry) β kernel
ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββΌβββββββββββββββββ
Startup time β ~100ms β ~200ms β ~1-2s
ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββΌβββββββββββββββββ
Memory overhead β ~5MB β ~50-100MB β ~128-256MB
ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββΌβββββββββββββββββ
Syscall compat β 100% β ~80% (growing) β ~100%
ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββΌβββββββββββββββββ
Performance β Near-native β Varies (I/O hit) β Near-native
β β β (after start)
ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββΌβββββββββββββββββ
GPU support β Full β Limited β VFIO passthrough
ββββββββββββββββββΌβββββββββββββββββΌββββββββββββββββββΌβββββββββββββββββ
Use case β Trusted apps β Untrusted code, β Hard multi-tenancy,
β β CI/CD, serverlessβ compliance, secrets
ββββββββββββββββββ΄βββββββββββββββββ΄ββββββββββββββββββ΄βββββββββββββββββCommon Issues
Pod stuck in ContainerCreating with RuntimeClass
- Cause: Handler not installed on node; or node doesnβt have required label
- Fix: Verify gVisor/Kata installed on node; check
scheduling.nodeSelectormatches
βoperation not supportedβ errors inside gVisor container
- Cause: gVisor doesnβt support all syscalls (e.g.,
io_uring, someioctl) - Fix: Check gVisor compatibility matrix; fall back to runc for incompatible workloads
Kata pod fails with βno hardware virtualization supportβ
- Cause: Node doesnβt have KVM/VT-x enabled
- Fix: Enable virtualization in BIOS; or use cloud instances with nested virtualization
Performance degradation with gVisor
- Cause: File I/O intensive workloads hit gVisorβs user-space filesystem overhead
- Fix: Use
directfsgVisor option for better I/O; or use Kata for I/O-heavy workloads
Best Practices
- Use RuntimeClass (not runtime annotations) β the standard K8s API since 1.20
- gVisor for untrusted code β CI builds, user-submitted code, serverless functions
- Kata for hard multi-tenancy β when namespace isolation isnβt sufficient
- Set overhead in RuntimeClass β accounts for sandbox memory in scheduling
- Node selectors on RuntimeClass β only schedule sandboxed pods on prepared nodes
- Test syscall compatibility β validate your application works under gVisor before production
- Default runc for trusted workloads β no need to sandbox everything (overhead vs security)
Key Takeaways
- RuntimeClass (
node.k8s.io/v1) selects container runtime per-pod viaruntimeClassName - gVisor: user-space kernel intercepting syscalls β fast startup, some compatibility gaps
- Kata Containers: lightweight VM per pod β full syscall compatibility, higher overhead
- Both provide stronger isolation than default runc (namespaces + cgroups only)
- Configure handler in containerd config, create RuntimeClass, set
runtimeClassNamein pod - Use gVisor for untrusted code; Kata for compliance/hard multi-tenancy; runc for everything else

Recommended
Kubernetes Recipes β The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book βLearn by Doing
CopyPasteLearn β Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses βπ Deepen Your Skills β Hands-on Courses
Courses by CopyPasteLearn.com β Learn IT by Doing
