πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Security intermediate ⏱ 20 minutes K8s 1.28+

gVisor RuntimeClass on K8s: Sandbox Pods

Deploy gVisor sandbox containers on Kubernetes using RuntimeClass. Install runsc, configure containerd, and isolate untrusted workloads with application-le.

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: gVisor (runsc) provides a user-space kernel that intercepts system calls, isolating containers from the host kernel. Install runsc on nodes, configure containerd with a runsc handler, create a RuntimeClass named gvisor, then deploy pods with runtimeClassName: gvisor. Ideal for running untrusted code, multi-tenant workloads, or CI/CD build containers.

The Problem

Standard containers share the host Linux kernel β€” a kernel exploit in any container compromises the entire node. Kubernetes NetworkPolicies and PodSecurity restrict network and API access, but don’t protect against kernel vulnerabilities. gVisor interposes an application-level kernel (Sentry) between the container and the host, reducing the attack surface from ~400 syscalls to a hardened subset.

flowchart TB
    subgraph STANDARD["Standard Container"]
        APP1["Application"] -->|"400+ syscalls"| HOST_K["Host Linux Kernel<br/>⚠️ Shared attack surface"]
    end
    
    subgraph GVISOR["gVisor Sandbox"]
        APP2["Application"] -->|"Intercepted syscalls"| SENTRY["Sentry<br/>(user-space kernel)"]
        SENTRY -->|"~70 filtered syscalls"| HOST_K2["Host Linux Kernel<br/>βœ… Reduced attack surface"]
    end

The Solution

Install gVisor on Nodes

# Install runsc binary (on each node)
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | \
  sudo tee /etc/apt/sources.list.d/gvisor.list
sudo apt-get update && sudo apt-get install -y runsc

# Verify installation
runsc --version

Configure containerd

# /etc/containerd/config.toml β€” add gvisor runtime 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"
# Restart containerd
sudo systemctl restart containerd

# Verify handler exists
sudo ctr plugins ls | grep runsc

Create RuntimeClass

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc
overhead:
  podFixed:
    memory: "64Mi"                   # Sentry overhead
    cpu: "50m"
scheduling:
  nodeSelector:
    gvisor.io/enabled: "true"        # Only schedule on gVisor nodes

Deploy Sandboxed Pods

# Untrusted workload β€” runs in gVisor sandbox
apiVersion: v1
kind: Pod
metadata:
  name: untrusted-workload
  labels:
    sandbox: gvisor
spec:
  runtimeClassName: gvisor           # Use gVisor sandbox
  containers:
    - name: app
      image: nginx:1.27
      ports:
        - containerPort: 80
      resources:
        requests:
          memory: 128Mi
          cpu: 100m
---
# CI/CD build runner β€” sandboxed to prevent breakouts
apiVersion: batch/v1
kind: Job
metadata:
  name: ci-build
spec:
  template:
    spec:
      runtimeClassName: gvisor
      containers:
        - name: builder
          image: docker.io/library/golang:1.22
          command: ["go", "build", "-o", "/output/app", "."]
          volumeMounts:
            - name: source
              mountPath: /workspace
      restartPolicy: Never
      volumes:
        - name: source
          configMap:
            name: build-source

Enforce gVisor for Specific Namespaces

# Use admission webhook or policy engine to enforce RuntimeClass
# Example: Kyverno policy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-gvisor-sandbox
spec:
  validationFailureAction: Enforce
  rules:
    - name: require-gvisor
      match:
        resources:
          kinds: ["Pod"]
          namespaces: ["untrusted", "ci-builds"]
      validate:
        message: "Pods in untrusted namespaces must use gVisor sandbox"
        pattern:
          spec:
            runtimeClassName: gvisor

gVisor vs Other Runtimes

Featurerunc (default)gVisor (runsc)Kata Containers
IsolationNamespace/cgroupUser-space kernelVM
OverheadNone~50m CPU, 64Mi RAM~128Mi RAM, 1-2s boot
Syscall filteringseccomp onlyFull interceptionFull VM
PerformanceNative5-20% overhead2-10% overhead
GPU supportβœ…βš οΈ Limitedβœ…
Network perfNative~90% native~95% native
Best forTrusted workloadsUntrusted code, multi-tenantConfidential computing

Verify Sandbox

# Check that pod is actually running in gVisor
kubectl exec -it untrusted-workload -- dmesg 2>&1 | head -3
# Output should show gVisor kernel, not host kernel:
# [    0.000000] Starting gVisor...
# [    0.000000] Daemonizing children...

# Check system calls are intercepted
kubectl exec -it untrusted-workload -- cat /proc/version
# Linux version 4.4.0 (gVisor)

# Verify on node
sudo runsc list

Common Issues

IssueCauseFix
RuntimeClass not foundrunsc not installed on nodeInstall runsc + configure containerd
Pod stuck in ContainerCreatingcontainerd handler misconfiguredCheck containerd config.toml + restart
App doesn’t work in gVisorUnsupported syscallCheck runsc debug logs; some apps need runc
Network performance dropgVisor netstackUse --network=host passthrough (reduces isolation)
Volume mount issuesgVisor filesystem overlayCheck gVisor compatibility with storage driver

Best Practices

  • Use gVisor for untrusted workloads β€” CI/CD builds, user-submitted code, multi-tenant
  • Don’t use gVisor for everything β€” overhead is unnecessary for trusted internal services
  • Label gVisor nodes β€” use nodeSelector to schedule only on capable nodes
  • Test application compatibility β€” some apps use unsupported syscalls (raw sockets, io_uring)
  • Set resource overhead β€” account for Sentry memory in RuntimeClass overhead
  • Monitor with runsc debug β€” provides gVisor-specific diagnostics

Key Takeaways

  • gVisor interposes a user-space kernel between containers and the host
  • Reduces host kernel attack surface from ~400 to ~70 syscalls
  • Deploy via RuntimeClass: gvisor β€” no application changes needed
  • 5-20% performance overhead β€” acceptable for security-sensitive workloads
  • Ideal for multi-tenant platforms, CI/CD runners, and untrusted code execution
  • Combine with NetworkPolicy and PodSecurity for defense-in-depth
#gvisor #runtimeclass #sandbox #containerd #workload-isolation
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens