NewCompare CPU & GPU pricing across AWS, Azure & GCP

gVisor

Run tenant workloads under the gVisor userspace kernel — pods that opt in with runtimeClassName never touch the host kernel.

gVisor

gVisor is a userspace kernel: an OCI runtime (runsc) that intercepts your container's syscalls and services them in software, so a compromised tenant container never reaches the host's Linux kernel. It is the kernel-isolation layer for clusters that run untrusted or multi-tenant code — CI runners, AI code execution, customer-supplied workloads — without giving each tenant its own VM.

DevZero turns it into a node policy setting. Turn on Sandbox runtime: gVisor on a node policy and DevZero builds the pool, installs gVisor on its nodes, and publishes the devzero-gvisor RuntimeClass. A workload opts in with one line, nothing else in its spec changes:

spec:
  runtimeClassName: devzero-gvisor

How it works

Everything below happens automatically once the node policy is enabled — there is nothing to install by hand.

  1. The pool is compiled with the sandbox label. DevZero's node operator provisions ordinary cloud VMs and stamps them with devzero.io/sandbox-runtime=gvisor, so the nodes are indistinguishable from the rest of your fleet except for the runtime they carry.
  2. The installer converges each node. A per-node installer (shipped with the node operator, enabled by default) downloads the pinned gVisor release, registers the runsc runtime handler with containerd, restarts it, and only then releases the node for tenant pods. A node that fails install stays cordoned for sandboxed workloads rather than running them unsandboxed.
  3. The RuntimeClass places the pod. Setting runtimeClassName: devzero-gvisor injects the node selector for the sandbox pool at admission — no node selectors or tolerations on the workload.
  4. Scale follows demand. The pool is sized by the same autoscaling as every other DevZero pool: nodes appear when sandboxed pods are pending and are consolidated away when they are gone.

Quickstart

Requires the DevZero workload operator v0.2.33 or later. The gVisor installer ships with it and is enabled by default — nothing to configure.

1. Create a node policy with the sandbox runtime

In the optimization dashboard, create a node policy as you normally would — instance requirements, zones, disruption — and set Sandbox runtime to gVisor. Point it at your cluster.

Within a minute the pool appears in your cluster:

kubectl get nodepool
kubectl get runtimeclass devzero-gvisor

2. Run a sandboxed workload

apiVersion: v1
kind: Pod
metadata:
  name: gvisor-proof
spec:
  runtimeClassName: devzero-gvisor
  containers:
  - name: proof
    image: busybox:1.36
    command: ["sh", "-c", "cat /proc/version && sleep 3600"]

The pod lands on a sandbox node and sees gVisor's synthetic kernel:

kubectl exec gvisor-proof -- cat /proc/version
# Linux version 4.19.0-gvisor #1 SMP ...

An identical pod without runtimeClassName on the same cluster still runs on ordinary nodes — gVisor is per-workload, never cluster-wide.

On clusters where AKS Sandbox or GKE Sandbox is enabled, the provider's own gvisor RuntimeClass and DevZero's devzero-gvisor coexist: the provider's schedules onto the provider-managed gvisor pool, DevZero's schedules onto the pool your node policy compiled. A workload opts into one or the other by name.

Keeping sandbox capacity for sandboxed workloads

Sandbox nodes are ordinary capacity, so non-sandboxed pods can schedule onto them when there is room. When a sandboxed pod would otherwise wait for a new node, you can have it bump the non-sandboxed pods instead — the scheduler's own preemption, triggered by naming the priority class DevZero compiles with the pool:

spec:
  runtimeClassName: devzero-gvisor
  priorityClassName: devzero-sandbox-gvisor

Preemption evicts the minimal set of lower-priority pods from one node and ignores PodDisruptionBudgets. Pods with a priority at or above the class's are never bumped, and platform DaemonSets are always out of reach.

Without the priority class, behavior is unchanged: a pending sandboxed pod gets a new node in under two minutes and consolidation reclaims it later.

Coexisting with provider gvisor

The RuntimeClass is named devzero-gvisor — not gvisor — deliberately. AKS Sandbox and GKE Sandbox both ship their own RuntimeClass named gvisor on clusters where those features are enabled, and .handler is immutable, so a DevZero object of the same name could never apply. With the prefix, both coexist on the same cluster: the provider's gvisor places pods onto the provider's own gvisor pool, and DevZero's devzero-gvisor places pods onto the pool your node policy compiled. A workload chooses which pool it runs on by naming the RuntimeClass.

Requirements and limits

  • Architecture: amd64 only. The compile step pins the pool to amd64 and refuses explicit arm64, which is unvalidated.
  • Egress: the installer downloads the pinned gVisor release from GitHub (github.com, objects.githubusercontent.com). Restricted networks need a mirror or a preloaded image.
  • Node OS: any standard Linux node image with an editable containerd config — Ubuntu, Amazon Linux 2023, and Azure Linux all work; both containerd 1.x and 2.x are supported. Bottlerocket, Talos, and other immutable-OS images are not supported. On GKE, use a Ubuntu image family in the GCENodeClass (the default Container-Optimized OS is not).
  • Overhead: each sandboxed pod pays a per-pod memory overhead (128Mi by default) and gVisor taxes syscall-heavy workloads, particularly file and network I/O. Position sandbox pools for untrusted-code isolation, not general compute.
  • Kata, alternatively: on AKS clusters with the Machine API provision mode, Microsoft's managed Kata Pod Sandboxing (workloadRuntime: KataVmIsolation) provides hardware-VM isolation; gVisor is DevZero's cross-cloud option.
  • Supported clouds: AWS, Azure, and GCP. The same node policy field, the same installer, the same RuntimeClass name on every cloud — only the provider's NodeClass section differs:
# Node policy → AWS section (EC2NodeClass)
sandbox: gvisor
aws:
  amiFamily: "AL2023"
  role: "arn:aws:iam::123456789012:role/KarpenterNodeRole-my-cluster"
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: "my-cluster"
  securityGroupSelectorTerms:
    - tags:
        karpenter.sh/discovery: "my-cluster"
# Node policy → Azure section (AKSNodeClass)
sandbox: gvisor
azure:
  imageFamily: "Ubuntu2204"
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: "my-cluster"
# Node policy → GCP section (GCENodeClass)
sandbox: gvisor
gcp:
  serviceAccount: "karpenter-sa@my-project.iam.gserviceaccount.com"
  imageSelectorTerms:
    - alias: "ubuntu@latest"   # Ubuntu required; the default COS is not
  networkConfig:
    network: "projects/my-project/global/networks/my-vpc"
    subnetwork: "projects/my-project/regions/us-central1/subnetworks/my-subnet"

The cloud-specific sections are the same fields you'd set on any DevZero node policy for that cloud — the sandbox runtime adds nothing extra to configure.

Verifying a sandboxed pod

The strongest in-pod signals, in order:

  1. cat /proc/version reports 4.19.0-gvisor (the host kernel reports its own, different version).
  2. The node's crictl info lists the runsc runtime handler.
  3. A functional smoke test — curl a service from inside the pod — proves gVisor's network stack, not just sandbox boot.

The node operator's health heartbeat includes a sandbox_gvisor_installer component: a node that stays in the installing state longer than ten minutes reports degraded, so a stuck install is visible in the dashboard instead of surfacing as pending pods.

On this page