
September 11, 2026
11 min read
By Kokil Thapa | Last reviewed: September 2026
RuntimeClass and Sandboxed Containers solve a problem standard Kubernetes pods cannot: strong isolation between container workloads and the host kernel. Default pods use runc and share the node kernel. A container escape or kernel bug can affect every pod on that node. For multi-tenant clusters, legal-tech portals with document uploads, or any workload running untrusted code, that risk is unacceptable. This guide explains how Kubernetes selects container runtimes through the CRI, how RuntimeClass wires alternate runtimes into scheduling, and what you should configure before putting sandboxed pods into production.
spec.runtimeClassName on pods that need stronger kernel isolation.What Are RuntimeClass and Sandboxed Containers in Kubernetes?
RuntimeClass is a Kubernetes API resource introduced in v1.12 and promoted to stable in v1.20. It tells the kubelet which container runtime handler to use when starting a pod. Sandboxed containers run inside an extra isolation layer—a user-space kernel like gVisor, or a lightweight VM like Kata—rather than calling the host kernel directly through runc.
Think of RuntimeClass as a label the scheduler and kubelet understand. You define a handler name that matches what containerd or CRI-O exposes. Pods that reference that RuntimeClass land only on nodes where the handler is installed and working.
The three sandbox runtimes you will encounter most often are gVisor, Kata Containers, and Firecracker-backed Kata on AWS. gVisor intercepts syscalls in a user-space kernel called Sentry. Kata spins up a micro-VM per pod or container. Both reduce the attack surface compared to runc, but they add CPU and memory overhead.
On clusters I help maintain for Linux server and deployment work, RuntimeClass rarely appears on day one. Teams add it after their first security review flags shared-kernel risk. That timing is normal. Plan the node pool before you flip production traffic.
Core RuntimeClass fields
A minimal RuntimeClass object names the handler and optionally restricts scheduling:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
scheduling:
nodeSelector:
sandbox.gvisor.io/enabled: "true"
tolerations:
- key: sandbox.gvisor.io/enabled
operator: Equal
value: "true"
effect: NoSchedule The handler value must match the runtime handler registered in containerd (/etc/containerd/config.toml) or CRI-O. The optional scheduling block injects nodeSelector and tolerations into pods that use this class. That keeps sandbox pods off generic worker nodes.
How Do You Configure RuntimeClass for gVisor or Kata Containers?
Configuration happens in three layers: install the sandbox runtime on nodes, register a handler with your CRI shim, then create the RuntimeClass and pod specs. Skipping any layer produces pods stuck in ContainerCreating with opaque CRI errors.
Step 1: Register the handler in containerd
For gVisor with containerd, add a runtime handler pointing to runsc:
[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" Restart containerd after editing the config. Confirm the handler appears:
ctr plugins ls | grep runsc Step 2: Label and taint sandbox nodes
Dedicate a node pool for sandbox workloads. Generic Laravel or API pods should stay on standard nodes. Sandbox overhead is real.
- Install gVisor or Kata on each sandbox node.
- Label nodes:
kubectl label node worker-sandbox-1 sandbox.gvisor.io/enabled=true - Add a NoSchedule taint so only tolerant pods schedule there.
- Create the RuntimeClass with matching nodeSelector and tolerations.
- Reference
runtimeClassName: gvisorin pod templates.
Step 3: Assign RuntimeClass to a pod
apiVersion: v1
kind: Pod
metadata:
name: untrusted-worker
spec:
runtimeClassName: gvisor
containers:
- name: worker
image: myregistry.io/jobs/processor:1.4.2
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "1"
memory: "1Gi" Deployments, Jobs, and CronJobs accept runtimeClassName under spec.template.spec. The field is immutable on running pods. You must recreate the workload to change runtime class.
Managed Kubernetes simplifies parts of this. GKE Sandbox uses gVisor through Pod annotations and node auto-provisioning. EKS and self-managed clusters require manual handler setup. Always read your cloud provider notes before assuming RuntimeClass names.
When Should You Use Sandboxed Containers Instead of Standard runc?
Not every pod belongs in a sandbox. Default runc is faster and simpler. Sandboxed containers earn their cost when the blast radius of compromise must shrink.
| Criteria | Standard runc | Sandboxed (gVisor / Kata) |
|---|---|---|
| Kernel isolation | Shared host kernel | User-space kernel or micro-VM boundary |
| Performance overhead | Low — native syscalls | Moderate to high — syscall interception or VM boot |
| Startup latency | Sub-second typical | gVisor: small penalty; Kata: higher cold start |
| Compatibility | Full Linux syscall surface | Some syscalls unsupported or emulated |
| Best fit | Trusted internal services | Multi-tenant, untrusted code, strong compliance |
| Ops complexity | Default everywhere | Separate node pool, monitoring, tuning |
Use sandboxed containers when you run third-party plugins, user-supplied scripts, PDF converters, or CI job runners inside the cluster. Legal-tech portals with client document processing are a concrete example from my work. Untrusted uploads should not share a kernel with your database pod.
Skip sandboxes for latency-sensitive paths. Payment callback handlers, Redis-backed session stores, and high-QPS API gateways usually stay on runc. You can still isolate them with network policies and resource limits.
Compliance teams sometimes mandate sandboxes for PCI or SOC 2 scope reduction. RuntimeClass gives auditors a clear control: this Deployment spec field proves the workload never scheduled without the alternate runtime. Document the RuntimeClass name in your security baseline.
gVisor vs Kata at a glance
- gVisor — Lower memory footprint per pod. Good density on smaller nodes. Watch for syscall compatibility with glibc, FUSE, and certain Go runtime features.
- Kata Containers — Hardware-virtualization isolation. Stronger boundary, closer to a tiny VM. Needs nested virt or bare metal. Higher per-pod overhead.
- Managed wrappers — GKE Sandbox, EKS Fargate (different model), and similar products hide handler wiring but still impose compatibility limits.
Benchmark both on your actual container images before choosing. A WooCommerce plugin stack behaves differently from a slim Alpine job runner. Use the same JSON formatter you use for API debugging to inspect RuntimeClass status returned by kubectl get runtimeclass -o json.
How Do RuntimeClass and Pod Security Work Together?
RuntimeClass is not a replacement for Pod Security Standards, seccomp, or AppArmor. It complements them. PSS restricts capabilities, volumes, and privilege escalation. RuntimeClass changes which kernel answers syscalls. You need both layers for defense in depth.
Enable the RuntimeClass admission plugin if you run a custom API server build. Most distributions enable it by default on supported versions. Without it, RuntimeClass objects exist but scheduling hints may not apply correctly.
Pair sandbox pods with restricted PSS at the namespace level:
apiVersion: v1
kind: Namespace
metadata:
name: untrusted-jobs
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted Mutating admission webhooks can inject runtimeClassName automatically. That prevents developers from forgetting the sandbox on sensitive namespaces. See admission controllers and validating webhooks for the pattern. A validating webhook can reject pods in untrusted-jobs that omit the gVisor RuntimeClass.
Scan images before they reach sandbox nodes. A sandbox containing a backdoored image is still a compromised process. It just cannot easily pivot through the kernel. Combine RuntimeClass with container image scanning in CI and a private registry like Harbor.
Rootless containers reduce host compromise risk from privileged daemon accounts. Sandboxes reduce kernel sharing risk inside the pod boundary. They solve different problems. Do not treat either as a full substitute for the other.
What Are Common RuntimeClass and Sandboxed Container Mistakes?
Most production failures trace back to scheduling, compatibility, or missing observability—not the RuntimeClass YAML itself.
Mistake 1: Handler name mismatch
The RuntimeClass handler must exactly match the CRI registration. runsc vs gvisor typos leave pods in ContainerCreating forever. Check kubelet logs:
journalctl -u kubelet -f | grep -i runtime Mistake 2: Scheduling sandbox pods on generic nodes
Without nodeSelector, tolerations, or dedicated pools, the scheduler places gVisor pods on nodes that lack runsc. Use RuntimeClass scheduling or taints consistently. Mixed pools cause intermittent failures that are painful to debug.
Mistake 3: Assuming full Linux compatibility
gVisor does not implement every syscall. eBPF tooling, certain io_uring paths, and some database engines fail silently or crash on start. Run integration tests on sandbox nodes before cutover. Keep a runc fallback Deployment for workloads that fail compatibility checks.
Mistake 4: Ignoring overhead in capacity planning
Sandbox nodes handle fewer pods per CPU core. Autoscaling rules tuned for runc will under-provision sandbox pools. Add headroom: plan 20–40% extra CPU for gVisor-heavy fleets. Kata often needs more.
Mistake 5: No monitoring of sandbox-specific metrics
Track OOM kills, syscall emulation errors, and pod start latency separately for sandbox node pools. Standard node-exporter dashboards hide sandbox regressions until users complain.
For teams shipping enterprise applications on Kubernetes, document which Deployments require sandbox runtimes in your runbook. Onboarding developers should not guess from namespace names alone.
Projects like Adventure Third Pole Trek run trusted Laravel and Livewire code on standard runc nodes. Only auxiliary workers—image thumbnailing, virus scanning hooks, or imported user scripts—belong in a sandbox pool. That split keeps costs predictable.
Key Takeaways
- RuntimeClass tells Kubernetes which CRI handler—gVisor, Kata, or custom—to use instead of default runc.
- Install the sandbox runtime on dedicated nodes, register the handler in containerd or CRI-O, then set
runtimeClassNameon pod specs. - Use sandboxed containers for untrusted or multi-tenant workloads; keep latency-critical trusted services on runc.
- Combine RuntimeClass with Pod Security Standards, network policies, and image scanning—sandboxes are one layer, not the whole stack.
- Test syscall compatibility and benchmark overhead before migrating production traffic; keep a runc fallback for incompatible images.
- Monitor sandbox node pools separately and align autoscaling with higher per-pod resource cost.
People Also Ask
Is RuntimeClass enabled by default in Kubernetes?
RuntimeClass is a stable API resource on supported Kubernetes versions. The RuntimeClass admission plugin is enabled on most standard distributions. You still must create RuntimeClass objects and install sandbox handlers yourself. Nothing sandbox-related works out of the box without node-level setup.
Can you use RuntimeClass with Docker Desktop or kind?
Local clusters can run gVisor for testing if you install runsc and patch containerd config manually. kind and minikube support is possible but fiddly. Treat local sandbox clusters as compatibility labs, not performance benchmarks. Production tuning needs real node pools.
Does gVisor replace seccomp and AppArmor profiles?
No. gVisor provides kernel isolation. seccomp and AppArmor restrict which syscalls and paths a container uses inside that boundary. Apply restricted Pod Security Standards and seccomp profiles even on sandbox pods for defense in depth.
How does RuntimeClass differ from Kubernetes PodSecurityPolicy?
PodSecurityPolicy was removed in Kubernetes 1.25. Pod Security Standards replaced it for pod hardening. RuntimeClass selects the container runtime handler. PSS controls privileges and volumes. Use both: PSS for pod spec constraints, RuntimeClass for runtime isolation.
Ship Safer Workloads With the Right Runtime Boundary
RuntimeClass and Sandboxed Containers give you a practical knob for kernel isolation without abandoning Kubernetes. Start with one untrusted workload class—document converters, CI runners, or third-party plugins—and prove compatibility on a dedicated node pool. Expand only after metrics look stable.
If you are planning cluster hardening for a Laravel API, legal-tech portal, or multi-tenant SaaS platform, map trusted and untrusted paths first. Not every pod needs gVisor. The ones that do should be provably sandboxed through RuntimeClass, admission policy, and CI scanning.
Need help designing node pools, CRI config, or deployment pipelines for sandboxed workloads? Review our support and maintenance services or custom software development offerings. For broader container security reading, see scanning container images for vulnerabilities and deploying containers on Amazon ECS with Fargate. Official references: the Kubernetes RuntimeClass documentation, gVisor installation guide, and the RuntimeClass KEP.
Contact us to audit your current cluster layout, or explore more guides on the blog and about page.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

