gVisor (runsc) Self-Host Install
gVisor (runsc) Self-Host Install
Section titled “gVisor (runsc) Self-Host Install”Phase 3 Story S5 / Jira PB-54. Implements D4 (gVisor self-host; no GKE Sandbox dependency).
Overview
Section titled “Overview”The compute Helm chart ships a DaemonSet (runsc-installer) that:
- Downloads and verifies
runscfrom the official gVisor release bucket. - Installs it at
/usr/local/bin/runscon each K8s node. - Appends the
runscruntime block to/etc/containerd/config.tomland restarts containerd. - Registers a
RuntimeClassnamedgvisorcluster-wide (handler: runsc).
Sandbox pods (S6/PB-55) reference this RuntimeClass via runtimeClassName: gvisor in their pod spec.
Prerequisites
Section titled “Prerequisites”- Kubernetes 1.26+ (RuntimeClass
node.k8s.io/v1GA since 1.20). - containerd 1.5+ as the CRI (the default on GKE standard, EKS, AKS, and kind).
- Nodes must have
wget,sha512sum, andnsenteravailable in the installer image. The defaultbusybox:stable-glibcsatisfies all three. - The DaemonSet pod runs as
privileged: trueto write to/usr/local/binand restart containerd. This is intentional and necessary — gVisor node setup has no unprivileged install path.
Values
Section titled “Values”gvisor: enabled: true # set false to skip DaemonSet + RuntimeClass version: "20240212" # gVisor release date tag; update to pin a new version installer: image: repository: busybox tag: stable-glibc pullPolicy: IfNotPresent nodeSelector: {} # empty = all nodes tolerations: [] scheduling: {} # passed into RuntimeClass.scheduling; empty = unconstrainedScoping to a node pool (recommended for production)
Section titled “Scoping to a node pool (recommended for production)”gvisor: nodeSelector: cloud.google.com/gke-nodepool: sandbox-pool tolerations: - key: sandbox operator: Equal value: "true" effect: NoSchedule scheduling: nodeSelector: cloud.google.com/gke-nodepool: sandbox-pool tolerations: - key: sandbox operator: Equal value: "true" effect: NoScheduleWhen scheduling is set on the RuntimeClass, the K8s scheduler automatically places pods
with runtimeClassName: gvisor onto nodes that match those constraints — no per-pod
nodeSelector needed on sandbox pods.
Cloud-specific notes
Section titled “Cloud-specific notes”kind (local dev / CI)
Section titled “kind (local dev / CI)”kind uses containerd. The DaemonSet installs runsc successfully on kind nodes.
Caveats:
- kind nodes are Docker containers;
nsenter --mount=/proc/1/ns/mntresolves to the containerd process inside the kind node container, which is correct. - After
helm install, wait ~60 seconds for the DaemonSet init containers to complete before scheduling the smoke pod. - Verified against kind v0.22+ with K8s 1.29.
GCP standard-rwo (CI target — locked 2026-05-22)
Section titled “GCP standard-rwo (CI target — locked 2026-05-22)”GKE standard nodes run containerd. The DaemonSet installs runsc in the same way as kind.
No GKE Sandbox (--sandbox-type=gvisor) is used — the chart self-installs runsc, which is
the D4 cloud-agnostic constraint.
StorageClass used for workspace PVCs: standard-rwo (GCP Regional PD, ReadWriteOnce).
Set pvc.storageClassName: standard-rwo in values for GCP deployments (S6/PB-55).
Secrets required for GCP CI:
GCP_SA_KEY— service account JSON with GKE cluster admin + storage permissions.GCP_PROJECT_ID— GCP project ID hosting the cluster.
If these secrets are absent from the repository, the gvisor-smoke / gcp-standard-rwo
CI matrix leg is skipped with an explicit warning (see .github/workflows/gvisor-smoke.yml).
AWS gp3
Section titled “AWS gp3”Deferred to Phase 6 hardening per ADR-0015 + design doc §10 row S5.
AWS EKS uses containerd; the DaemonSet approach is identical. StorageClass would be gp3.
Troubleshooting
Section titled “Troubleshooting”DaemonSet pod stuck in Init:0/2
Section titled “DaemonSet pod stuck in Init:0/2”The init containers run sequentially. Init:0/2 means neither has finished.
kubectl logs -n paper-board <runsc-installer-pod> -c install-runsckubectl logs -n paper-board <runsc-installer-pod> -c configure-containerdCommon causes:
- Network unreachable —
storage.googleapis.comblocked. On air-gap clusters, host the gVisor binary in an internal mirror and overridegvisor.version+ the download URL (requires chart modification). - SHA512 mismatch — corrupt download. Re-run by deleting the pod; DaemonSet recreates it.
/etc/containerd/config.tomlmissing — add atype: FileOrCreatehostPath for the containerd config dir to auto-create the file.
RuntimeClass not found
Section titled “RuntimeClass not found”kubectl get runtimeclass gvisorIf missing, check helm/compute/templates/runtime-class.yaml is rendered:
helm template compute ./helm/compute --set gvisor.enabled=true | grep -A5 RuntimeClassSmoke pod fails: no RuntimeClass "gvisor"
Section titled “Smoke pod fails: no RuntimeClass "gvisor"”The DaemonSet pods may not have finished their init containers yet. Wait and re-check:
kubectl get ds -n paper-board -wSmoke pod fails: dmesg: read kernel buffer failed: Operation not permitted
Section titled “Smoke pod fails: dmesg: read kernel buffer failed: Operation not permitted”gVisor restricts some syscalls. dmesg works inside gVisor because gVisor provides its
own kernel ring buffer. If you see this error, the pod is running on a real Linux kernel
(not gVisor) — verify runtimeClassName: gvisor is set on the pod spec and the node
is in the RuntimeClass scheduling set.
containerd not restarted after config change
Section titled “containerd not restarted after config change”The configure-containerd init container sends SIGHUP to PID 1 on the host as a fallback
when systemctl is unavailable (e.g., in kind containers). If containerd still does not
load the runsc plugin:
# On the node:nsenter --mount=/proc/1/ns/mnt -- systemctl restart containerdVerifying the runtime class on a running pod
Section titled “Verifying the runtime class on a running pod”kubectl get pod <sandbox-pod> -o jsonpath='{.spec.runtimeClassName}'# expected output: gvisor
kubectl describe pod <sandbox-pod> | grep "Runtime Class"# expected: Runtime Class: gvisorUpgrading runsc
Section titled “Upgrading runsc”- Update
gvisor.versioninvalues.yamlto the new release date tag. - Bump
helm/compute/Chart.yamlversion. helm upgrade compute ./helm/compute— DaemonSet rolls out, init container detects the version mismatch and re-installs runsc on each node. Containerd is restarted.- Drain sandbox pods from the node before the DaemonSet pod reaches that node if zero- downtime is required (Phase 6 hardening concern).