gVisor and Kata Containers: Sandboxed Runtimes in Kubernetes

gVisor and Kata Containers: Sandboxed Runtimes in Kubernetes

A plain-English guide to running untrusted workloads safely, by putting an extra wall between your containers and the machine they run on.

Imagine you own a big apartment building. Every tenant has their own locked door, their own furniture and their own mail slot. But behind the walls, all the tenants share the same water pipes, the same electrical wiring and the same boiler in the basement. Most days, nobody notices. Then one tenant finds a way to tamper with the boiler, and suddenly every apartment in the building has a problem.

Containers in Kubernetes work a lot like that building. Each container feels private, but all of them on one machine share a single core piece of software called the operating system kernel. If someone breaks through that shared layer, they can reach everyone else. For years, teams accepted this risk because the workloads they ran were their own code, written by their own engineers.

That world has changed. Today, platforms run customer-submitted code, continuous integration jobs from strangers, plugins from third parties and even code written by AI models. For these cases, “a locked door” is not enough. You need a second wall. Two open-source projects, gVisor and Kata Containers, build that second wall in two very different ways. In this guide you will learn what they are, how they work inside, how Kubernetes lets you choose them, and when each one is the right tool.

1Why Normal Containers Are Not Enough

Before we meet the fixes, we need to understand the problem. Containers are wonderful, but they have one famous weak spot: they share a kernel.

What is a kernel, and why does everyone share it?

The kernel is the most trusted program on any computer. It sits between your applications and the hardware. When an application wants to read a file, send data over the network or start another process, it cannot touch the hardware directly. It asks the kernel to do the job on its behalf. Each of these requests is called a system call, or syscall for short.

A Linux container is not a tiny computer. It is just a normal group of Linux processes that has been given a restricted view of the system. Two Linux features create this restricted view. Namespaces control what a process can see, such as its own process list, its own network interfaces and its own file tree. Control groups, usually called cgroups, control how much a process can use, such as how much memory or CPU time it gets.

Notice what is missing from that list: a separate kernel. Every container on a node talks to the very same Linux kernel running on that node. That is why containers start in a fraction of a second and use so little memory. It is also why they are less isolated than virtual machines.

Everyday Analogy

Think of an apartment building. Each apartment has its own door and lock, which are the namespaces. The building also limits how much water each tenant can use, which is the cgroups. But the pipes, wires and boiler are shared by all. The kernel is the boiler room. A tenant who finds a way into the boiler room is no longer a tenant. They are the building manager.

The attack surface problem

An attack surface is the total set of places where an attacker can try to break something. For a standard container, the attack surface includes every system call that the Linux kernel offers. Linux has several hundred of them, and behind them sits tens of millions of lines of kernel code written over three decades. Code that big always contains bugs, and some of those bugs are security holes.

When a security hole in the kernel is found, a malicious process inside a container can sometimes use it to escape. Escaping means gaining control over the host machine and, from there, over every other container on it. This is not a theory. It has happened many times in the real world.

1
kernel shared by every container on a node
300+
system calls an attacker can reach
Tens of millions
of lines of kernel code in the way

Real escapes that made headlines

Here are a few well-known examples that show why teams worry about the shared kernel and the shared runtime.

  • Dirty COW (2016). A flaw in how the Linux kernel handled memory let an ordinary user write to files they should only be able to read. It affected almost every Linux system of its time, including container hosts.
  • runc CVE-2019-5736. A malicious container could overwrite the host’s copy of runc, the program that starts containers, and so run code as the host’s administrator.
  • Dirty Pipe (2022). Another kernel flaw, this time in how pipes handled data, allowed overwriting read-only files.
  • Leaky Vessels (2024). A set of runc flaws, including CVE-2024-21626, that could let a container reach the host file system through a leaked file handle.

Notice the pattern. Some of these bugs were in the kernel itself, and some were in the runtime that sets up containers. In both cases, the wall between the container and the host was only as strong as the shared code underneath it.

!
Watch Out

Containers were designed for packaging and resource sharing first, and isolation second. They are a good boundary between your own well-behaved services. They are a weaker boundary between mutually distrusting parties.

Who actually needs a stronger wall?

Not everyone does. If you run your own microservices, written by your own team, on your own cluster, regular containers plus good hygiene are usually fine. A stronger wall becomes valuable when the code you run is not fully trusted. Common cases include:

  • Multi-tenant platforms, where many customers share one cluster and you promise each one that others cannot reach them.
  • Serverless and function platforms, where strangers upload arbitrary code and you run it for them.
  • Continuous integration systems, where a build job may run a script pulled from a pull request opened by anyone.
  • Online coding playgrounds and notebooks, where users type code and press run.
  • AI agents and code interpreters, where a model writes code that your platform executes without a human reading it first.
  • Regulated workloads, where auditors want a strong, provable boundary around sensitive data.

For all of these, the answer is the same idea: put one more layer between the workload and the host kernel. That layer is what we call a sandbox.

2What a Sandboxed Runtime Really Is

A sandboxed runtime is a replacement for the part of the system that actually starts your container. It keeps everything else about containers the same, but changes how the container touches the host.

The sandbox idea in one sentence

A sandbox is a protective box around a program, built so that even if the program is hostile, it can only play inside the box. A children’s sandbox keeps sand from spreading across the garden. A software sandbox keeps a misbehaving program from spreading across the machine.

A sandboxed container runtime applies this idea to containers. You still build images the same way. You still write the same Kubernetes objects. Your application does not need to be rewritten. What changes is the engine underneath, which now adds an extra boundary between the container and the host kernel.

Two strategies: intercept or virtualize

There are two main ways to build that extra boundary, and these two ways are exactly what gVisor and Kata Containers represent.

Strategy A

Intercept and re-implement

Put a guard in front of the host kernel. The guard receives every request from the application and handles most of them by itself, using its own code. Only a very small number of safe requests ever reach the real kernel. This is gVisor.

Strategy B

Virtualize and isolate

Give the workload its own private kernel inside a very small virtual machine. Whatever happens inside stays inside, because the virtual machine is walled off by hardware features. This is Kata Containers.

Shared Goal

Shrink what the host exposes

Both approaches aim to make the host kernel reachable through a much narrower and better-guarded door than a normal container ever gets.

Shared Promise

Same Kubernetes experience

Both plug into Kubernetes through the standard runtime interface, so a pod still looks like a pod to the cluster, your tools and your teammates.

Everyday Analogy

Picture a bank. Strategy A is the teller behind bulletproof glass. You pass your request through a small slot, the teller handles it, and you never touch the vault. Strategy B is giving every customer their own small private office in a separate building, with their own staff, so nothing they do can ever reach another customer’s office. The glass is cheap and fast but the teller must understand every request. The private office is sturdier but costs more space and takes longer to open.

What a sandbox does not do

It is important to stay realistic. A sandboxed runtime does not replace good security practice. It will not fix a weak password, an over-permissive cloud role or a leaked secret. It does not stop an application from being hacked at the application level, for example through a SQL injection. What it does is limit the damage after that, by making it much harder for the compromised program to break out to the host and to its neighbors. Security experts call this defense in depth: many layers, so that one failure is not fatal.

flowchart TB
    subgraph RUNC["Regular container - runc"]
        A1["Application"] --> K1["Host Linux Kernel"]
    end
    subgraph GV["gVisor sandbox"]
        A2["Application"] --> S2["User-space kernel - Sentry"]
        S2 -->|"few filtered calls"| K2["Host Linux Kernel"]
    end
    subgraph KT["Kata Containers sandbox"]
        A3["Application"] --> G3["Guest Linux Kernel"]
        G3 --> H3["Hypervisor - VMM"]
        H3 --> K3["Host Linux Kernel"]
    end
Figure 1 – Three ways an application can reach the host: directly, through a guard, or through a private virtual machine.

Read the figure from top to bottom in each box. In the regular container, the application speaks straight to the host kernel. In gVisor, there is a guard program in the middle. In Kata, there is a whole second kernel and a hypervisor in the middle. The more layers there are, the harder an attack becomes, but each layer also adds some cost. We will weigh those costs later.

3How Kubernetes Starts a Container

To understand how a sandboxed runtime plugs in, you first need to see the chain of programs that turns a pod request into a running process.

The chain, link by link

When you ask Kubernetes to run a pod, no single program does all the work. Several programs hand the job along like workers on an assembly line. Each one has a clear role.

1

Kubelet

The kubelet is the agent running on each node. It watches the cluster for pods assigned to its node and decides what must be started or stopped.

2

CRI

The Container Runtime Interface is a standard set of calls the kubelet uses to talk to any compatible runtime. It lets Kubernetes stay neutral about which engine runs underneath.

3

High-level runtime

Programs like containerd or CRI-O receive CRI calls. They pull images, manage storage and network setup, and keep track of container state.

4

Shim

A small helper process that stays alive next to each pod. It lets the high-level runtime restart without killing running containers, and it reports back on exits.

5

Low-level OCI runtime

This is the program that really creates the isolated process. The default is runc. It follows the Open Container Initiative specification to set up namespaces and cgroups, then starts your application.

Where the sandbox plugs in

The clever part is that only the last two links need to change. Kubernetes, the kubelet and even containerd stay exactly as they are. To get a sandbox, you swap runc for a different low-level runtime that follows the same rules but isolates the workload differently.

  • For gVisor, the replacement is a runtime called runsc.
  • For Kata Containers, the replacement is a shim and runtime pair, usually referenced as kata, that starts a lightweight virtual machine.

Because the swap happens deep in the stack, your container images, your registry, your deployment tools and your monitoring dashboards keep working. This compatibility is the single biggest reason these projects are practical.

flowchart LR
    API["Kubernetes API Server"] --> KL["Kubelet on the node"]
    KL -->|"CRI calls"| CD["containerd or CRI-O"]
    CD --> SH["Runtime shim"]
    SH --> R1["runc - default"]
    SH --> R2["runsc - gVisor"]
    SH --> R3["Kata runtime - lightweight VM"]
    R1 --> W1["Regular container"]
    R2 --> W2["gVisor sandbox"]
    R3 --> W3["Kata micro VM"]
Figure 2 – Only the low-level runtime changes. Everything above it is the same Kubernetes you already know.
i
Good to Know

Newer containerd versions use a “shim v2” design where one shim process serves a whole pod. This fits sandboxed runtimes very well, because a pod maps neatly to one sandbox or one virtual machine.

A confusing word: “sandbox” used two ways

Here is a common trap for beginners. In Kubernetes, every pod already has something called a pod sandbox. This is simply the shared environment, namespaces and a tiny placeholder process often called the pause container, that all containers in the pod live in. It is a bookkeeping idea, not a security feature.

When people say sandboxed runtime or sandboxed container in a security conversation, they mean something stronger: an extra isolation layer such as gVisor or Kata. The two uses of the word are unrelated. Every pod has a pod sandbox, but only pods using a runtime like gVisor or Kata are security-sandboxed.

4Meet gVisor: A Kernel Written for Safety

gVisor takes the “guard at the glass window” approach. It places a brand-new kernel, written from scratch, between your application and the real one.

What is gVisor?

gVisor is an open-source project started at Google. Its core idea is surprisingly bold: write a second Linux-compatible kernel that runs as an ordinary program, not as part of the operating system. Google calls this an application kernel. Your container believes it is talking to Linux. In truth, it is talking to this program, which then decides what is safe to pass along to the real kernel underneath.

gVisor is written in Go, a memory-safe language. This matters a great deal. Many of the worst kernel bugs in history come from memory mistakes in the C language, such as reading past the end of a buffer. A memory-safe language makes whole families of those mistakes much harder to make.

Everyday Analogy

Think of a hotel concierge. Guests never walk into the hotel’s back offices to book a taxi or order a repair. They ask the concierge, who handles almost everything with the hotel’s own staff and phones the outside world only when truly needed. If a guest behaves strangely, the concierge simply refuses. The concierge is gVisor’s kernel, and the outside world is the real Linux kernel.

The main parts

Component

runsc

The command-line runtime that follows the OCI rules. This is the program containerd launches in place of runc. It sets up the sandbox and starts the other parts.

Component

Sentry

The heart of gVisor. It is the user-space kernel that implements system calls, memory management, process handling, signals, threads and more, all in its own code.

Component

Gofer

A helper process that opens files on the host on behalf of the sandbox. The Sentry is not allowed to open host files directly, so this helper acts as a tightly controlled file clerk.

Component

Netstack

gVisor’s own network stack, also written in Go. It handles protocols such as TCP and UDP inside the sandbox rather than leaning on the host’s network code.

Component

Platform

The mechanism used to catch system calls from the application. Common choices are the default systrap approach and a KVM-based approach for certain environments.

Safety Net

Seccomp filter

The Sentry itself is boxed in by a strict filter that allows only a small list of host system calls. Even if the Sentry were tricked, its reach would stay narrow.

flowchart TB
    APP["Your application in the container"] -->|"system calls"| SENTRY["Sentry - user-space kernel"]
    SENTRY --> NET["Netstack - own network stack"]
    SENTRY -->|"file requests"| GOFER["Gofer - file access helper"]
    SENTRY -->|"small filtered set of calls"| HOSTK["Host Linux Kernel"]
    GOFER --> HOSTK
    NET -->|"packets"| HOSTK
    SEC["Seccomp filter"] -.->|"restricts"| SENTRY
Figure 3 – gVisor components. The application never speaks directly to the host kernel.

Two layers of protection

gVisor gives you two walls, not one. First, an attacker must find a bug in the Sentry to do anything interesting. Second, even after finding one, the Sentry is itself restricted to a short list of host calls. To escape, an attacker would need to chain two separate weaknesses: one in the Go code of gVisor and one in the small slice of the host kernel that the Sentry is still allowed to reach. That is a far taller order than exploiting a single kernel bug.

i
Good to Know

gVisor does not claim to be bug-free. Its promise is narrower and more honest: it reduces the amount of host kernel code that untrusted programs can reach, and it uses a safer language for the code that does the guarding.

5Inside gVisor: The Life of a System Call

The best way to understand gVisor is to follow one small request, such as reading a file, from the moment the application asks to the moment it gets an answer.

Step by step

1

The application makes a system call

Your program wants to read a file, so it issues a read system call, exactly as it would on any Linux machine. It has no idea anything is different.

2

The platform intercepts it

Instead of the call landing in the host kernel, the chosen platform catches it and hands control to the Sentry. This is the moment the guard steps in.

3

The Sentry decodes and checks

The Sentry looks at which call was made, validates every argument, checks permissions using its own bookkeeping, and decides how to serve it.

4

The Sentry serves it, or asks for help

Many calls, such as memory allocation or inspecting process state, are answered completely inside the Sentry. File operations are forwarded to the Gofer, which opens and reads the file using narrow, pre-approved access.

5

The answer returns to the application

The Sentry places the result where the application expects it and resumes the program. From the program’s point of view, it simply called Linux and got an answer.

How the interception works

The platform is the piece that physically catches the call. Think of it as the doorbell that rings the concierge. Different platforms trade speed for portability.

  • Systrap is the modern default. It uses a combination of host features to trap system calls efficiently and works on ordinary machines, including cloud virtual machines, without special hardware access.
  • KVM platform uses the processor’s virtualization features to run the application in a special mode, which can make interception faster on bare-metal hosts where this is available.

Older versions also offered a ptrace-based platform that was easy to run but slower. Recent versions have moved on from it, so treat any advice that relies on it as dated.

Files and the network

File access deserves special attention because it is where many container escapes begin. In gVisor, the Sentry keeps its own in-memory picture of the file tree and talks to the Gofer over a dedicated protocol when it needs real files from the host. The Gofer is a separate process with a very limited set of permissions. Even if the application tricks the Sentry, the Gofer will only open what it has been told is allowed.

For networking, gVisor can use its own netstack. Packets are processed by gVisor’s Go code, and only raw packets cross to the host. This keeps the host’s network code, which has seen its share of vulnerabilities, further from the workload. There is also a mode that passes network calls through to the host stack for speed. That mode is faster but gives up part of the isolation benefit, so teams choose it deliberately.

The honest cost: compatibility

Because gVisor re-implements Linux rather than using Linux, it must catch up with every feature. It supports most commonly used system calls, which is enough for the vast majority of web services, databases and language runtimes. However, unusual calls, exotic file systems, and some specialized tools may be missing or behave slightly differently. A program that works on regular Linux may fail in gVisor because of one rarely used call. This is why testing your workload in the sandbox before rolling it out widely is non-negotiable.

!
Watch Out

Workloads that make huge numbers of system calls, or that move lots of small file or network operations, can slow down noticeably. Each call now takes a detour through the Sentry. Heavy computation that rarely calls the kernel is barely affected.

6Meet Kata Containers: A Tiny VM for Every Pod

Kata Containers takes the opposite road. It does not guard the kernel. It gives each pod its own private kernel inside a very small, very fast virtual machine.

What is Kata Containers?

Kata Containers is an open-source project that runs containers inside lightweight virtual machines while still behaving like a container runtime. It was formed in 2017 by merging two earlier efforts: Intel’s Clear Containers and Hyper’s runV. It is now hosted by the OpenInfra Foundation and has contributors from many large companies, with strong early involvement from firms such as Ant Group and Intel.

A virtual machine, or VM, is a pretend computer running inside a real one. The pretend computer has its own kernel. A hypervisor is the program that creates and manages such pretend computers, and it uses special processor features to keep each one strictly walled off. Ordinary VMs are heavy because they carry a full operating system and boot slowly. Kata trims the VM down to the bare minimum so that it boots in a fraction of a second and uses little memory.

Everyday Analogy

Back to the apartment building. A regular container is a rented apartment. A Kata pod is a small detached cottage with its own plumbing, wiring and boiler. If something goes wrong in one cottage, the neighbors never feel it, because nothing is shared except the plot of land underneath. The cost is that you need a bit more land and building a cottage takes longer than unlocking an apartment.

The main parts

Component

Kata shim and runtime

The program that plugs into containerd or CRI-O. It receives the pod request and orchestrates the creation of the virtual machine.

Component

Hypervisor or VMM

The virtual machine monitor that actually creates the VM. Kata can work with several, including QEMU, Cloud Hypervisor, Firecracker and a built-in option called Dragonball.

Component

Guest kernel

A small, tuned Linux kernel that runs inside the VM. It is stripped of features that a pod does not need, which keeps boot time and memory low.

Component

Guest root file system

A minimal image inside the VM, containing just enough to start the helper agent and then the containers.

Component

Kata agent

A small program inside the VM that takes commands from outside and creates the actual containers within the guest, using ordinary namespaces and cgroups.

Component

Virtio devices

Efficient virtual hardware, such as virtual network cards and shared file systems, that let the VM exchange data with the host in a controlled way.

flowchart TB
    CRI["containerd or CRI-O"] --> SHIM["Kata shim v2"]
    SHIM --> VMM["Hypervisor - QEMU, Cloud Hypervisor, Firecracker"]
    subgraph VM["Lightweight VM - one per pod"]
        GK["Guest Linux Kernel"]
        AG["Kata agent"]
        C1["Container A"]
        C2["Container B"]
        AG --> C1
        AG --> C2
        GK --- AG
    end
    VMM --> VM
    SHIM |"vsock channel"| AG
    VMM --> HK["Host Linux Kernel and KVM"]
Figure 4 – Kata Containers: one lightweight VM per pod, with an agent inside managing the containers.

One VM per pod, not per container

A key design choice is that Kata creates a VM for the whole pod, not for each container. This matches how Kubernetes thinks. All containers in a pod already share a network and can share storage, so they all live together in the same VM. The boundary that Kata protects is between pods, which is exactly where trust boundaries usually sit.

7Inside Kata: How a Pod Becomes a Micro VM

Now let us follow a pod from request to running container and see where the isolation comes from.

The lifecycle

1

Containerd calls the Kata shim

The high-level runtime sees that this pod asks for the Kata runtime and starts the Kata shim instead of the default one.

2

The hypervisor creates the VM

The shim asks the chosen hypervisor to build a small virtual machine with a defined amount of memory and CPU, using the hardware virtualization features of the processor.

3

The guest kernel boots

The tiny kernel starts inside the VM. Because it is stripped down and tuned, this step is fast, commonly well under a second on modern hardware.

4

The agent comes alive

The Kata agent starts and opens a private communication channel to the shim. The channel is usually vsock, a special host-to-guest link that does not use the normal network.

5

The image is shared into the guest

The container’s file system is made available inside the VM, typically using a shared file system called virtio-fs or a block device that holds the image layers.

6

The agent starts the containers

Inside the guest, the agent creates namespaces and cgroups and launches your application, exactly like a normal container, except that every system call now lands in the guest kernel rather than the host’s.

Why this is strong

When your application makes a system call, the guest kernel answers it. The host kernel never sees that call. For a program to harm the host, it would first need to find a bug in the guest kernel, which only gets it control of the guest. Then it would need a second bug in the hypervisor or in the narrow set of virtual devices, to cross from the guest to the host. Hardware virtualization is one of the most heavily tested isolation mechanisms in computing, and it is what public clouds already rely on to separate customers from each other.

Networking and storage

Networking is a tricky part, because Kubernetes expects each pod to have a network identity prepared by a network plugin outside the VM. Kata bridges this gap by connecting the virtual network card inside the VM to the pod’s network setup on the host, so that traffic flows as the cluster expects. From outside, the pod still looks and behaves like any other pod, with its own IP address.

For storage, volumes mounted into a pod must cross the VM boundary. Kata handles this with shared file system mechanisms or by handing the VM a virtual disk. This crossing is a place where performance can differ from regular containers, so file-heavy workloads should be measured.

Resources and overhead

A VM carries a fixed cost: the guest kernel, the agent and the hypervisor itself use memory and CPU even if your application is idle. Kata can resize the VM as containers are added, adding CPU and memory while it runs. Still, this fixed cost is real, and Kubernetes has a field to account for it, which we will meet in the next chapter.

!
Watch Out

Kata needs hardware virtualization support. On a bare-metal server this is usually available. On a cloud virtual machine, you need a machine type that allows nested virtualization, which means a VM inside a VM. Not every cloud instance type permits this, so check before you plan a cluster around it.

i
Good to Know

The same VM-based design underpins the Confidential Containers effort, which combines Kata with hardware features that encrypt memory so that even the host administrator cannot read a pod’s data. It builds on the isolation idea you just learned.

8RuntimeClass: How a Pod Picks Its Runtime

You now know the two sandboxes. The next question is practical: how do you tell Kubernetes, pod by pod, which one to use?

What is a RuntimeClass?

A RuntimeClass is a cluster-wide Kubernetes object that gives a friendly name to a runtime configuration. Think of it as a menu item. The kitchen, which is the node, knows how to prepare each dish. The customer, which is the pod, just orders by name. RuntimeClass became generally available in Kubernetes 1.20, so every supported cluster today has it.

A RuntimeClass has three important parts.

Field 1

handler

The name of the runtime as configured on the node. It must match a handler name registered in containerd or CRI-O. If the names do not match, the pod cannot start.

Field 2

overhead

The extra CPU and memory that the sandbox itself consumes. The scheduler adds this to the pod’s own requests so that nodes are not over-packed. This matters most for Kata, which has a fixed VM cost.

Field 3

scheduling

Node selectors and tolerations that guide the pod to nodes that actually have the runtime installed. This prevents a gVisor pod from landing on a node that cannot run it.

In the Pod

runtimeClassName

A single field in the pod specification. Setting it to the name of a RuntimeClass is all a developer must do to request the sandbox. Leaving it empty means the default runtime.

The full journey

1

Install the runtime on chosen nodes

A platform engineer installs gVisor or Kata on a set of nodes, usually a dedicated group, and does not touch the rest.

2

Register a handler

In the containerd or CRI-O settings on those nodes, a handler name such as gvisor or kata is mapped to the new runtime.

3

Mark the nodes

The sandbox nodes get a label so selectors can find them, and often a taint so that ordinary pods stay away and do not waste the expensive capacity.

4

Create the RuntimeClass

A cluster administrator creates the object with the handler name, the overhead values, and the scheduling hints for the labeled and tainted nodes.

5

Request it from a pod

A workload sets runtimeClassName. An admission step automatically merges the overhead and scheduling rules into the pod, so developers do not need to repeat them.

6

Verify

The team checks that the pod is running on a sandbox node and that the sandbox is really in use, for example by looking at the kernel version seen from inside the pod, which differs under gVisor and Kata.

sequenceDiagram
    participant Dev as Developer
    participant API as API Server
    participant Sch as Scheduler
    participant Kub as Kubelet
    participant Run as containerd
    participant Sbx as Sandbox runtime
    Dev->>API: Create pod with runtimeClassName
    API->>API: Admission adds overhead and node rules
    API->>Sch: Pod needs scheduling
    Sch->>Kub: Assign to a sandbox-capable node
    Kub->>Run: Create pod sandbox via CRI
    Run->>Sbx: Start with matching handler
    Sbx-->>Kub: Sandbox and containers running
Figure 5 – From pod request to sandboxed container. The only thing the developer wrote was the runtime class name.
i
Good to Know

Cluster policy tools such as admission controllers can enforce that certain namespaces, for example one that runs customer code, must always use a sandbox RuntimeClass. This turns a good habit into a guarantee that no one can forget.

Common failures at this stage

  • Handler name mismatch. The RuntimeClass names a handler that the node does not know. The pod stays stuck in a creating state, and the event log mentions an unknown runtime.
  • Missing node selection. The scheduler places the pod on a node without the runtime. Always pair the RuntimeClass with scheduling rules.
  • Forgotten overhead. Without it, nodes look emptier than they are, and pods can get squeezed or evicted under load.
  • Unsupported pod settings. Privileged mode, sharing the host network or process space, and some host-path mounts conflict with the whole point of a sandbox and may be rejected or ignored.

9gVisor vs Kata vs runc: Side by Side

Now that you know how each one works, here is a direct comparison. The figures here are directional, since real numbers depend heavily on your hardware and workload.

Aspectrunc (regular)gVisorKata Containers
Isolation methodNamespaces and cgroupsUser-space kernel intercepting callsSeparate guest kernel in a lightweight VM
Kernel the app talks toHost kernelSentry, written in GoGuest Linux kernel
Host kernel exposureFull system call surfaceSmall filtered setMostly the hypervisor and virtual devices
Start-up timeVery fastFast, close to regularSlower, due to VM boot
Fixed memory overheadNegligibleSmallNoticeable per pod
Linux compatibilityCompleteVery good, with gapsVery high, since it is a real Linux kernel
CPU-heavy workNative speedNear nativeNear native
System call, file and network heavy workNative speedCan be slowerModerate cost from virtualization
Hardware needsNone specialWorks on ordinary hostsHardware virtualization, or nested virtualization in the cloud
Best fitTrusted internal servicesMulti-tenant platforms, many short tasksStrongest isolation, regulated and sensitive workloads

Reading the table like an architect

No column wins every row. A regular container is the fastest and simplest, but has the weakest wall. gVisor gives a strong wall for a modest price, but you pay with compatibility questions and slower system-call-heavy work. Kata gives the strongest wall and the highest compatibility, but you pay with start-up time, per-pod memory, and a hardware requirement.

Everyday Analogy

Choosing a runtime is like choosing how to ship a parcel. Regular post is cheap and quick, and fine for a birthday card. A courier with tamper-proof packaging costs a little more and suits valuables, though not every odd-shaped item fits their rules. Hiring an armored truck with its own driver is safest for jewels, but it is slow and expensive for ordinary mail. You match the protection to the value of what you carry.

A simple way to decide

Choose runc

You own and trust the code

Internal services from your own team, with strong pod security settings, rarely need a sandbox. Keep things simple.

Choose gVisor

Many small, untrusted tasks

Function platforms, notebooks, build jobs and plugin hosts benefit from its light weight and quick starts, if your programs fit its supported calls.

Choose Kata

You need the hardest boundary

Hostile or regulated workloads, or programs that need full Linux behavior and specialized kernel features, suit a real kernel in a real VM.

Mix them

Different pools for different risk

Many clusters offer all three through separate RuntimeClasses, and let teams pick based on the sensitivity of each workload.

10Advantages, Disadvantages and Trade-offs

Every engineering choice is a trade. Here is an honest ledger for each sandbox, so you can decide with open eyes.

gVisor

Advantages

  • Big reduction in host kernel exposure, with a second layer around the Sentry itself.
  • Memory-safe Go implementation avoids a whole class of memory bugs.
  • Light on resources, so many sandboxes fit on one node.
  • Works on ordinary machines without needing hardware virtualization.
  • Quick to start, which suits short-lived tasks.

Disadvantages

  • Not every Linux feature or system call is available.
  • Syscall-heavy, file-heavy and network-heavy work can slow down.
  • Some low-level tools and specialized devices may not work as expected.
  • Debugging can be harder because the usual host-level tools see less.
  • Still shares the host kernel for the few calls it passes through.

Kata Containers

Advantages

  • Hardware-enforced isolation with a separate guest kernel per pod.
  • Runs a real Linux kernel, so compatibility is excellent.
  • Choice of hypervisors, so teams can pick a trade between speed and features.
  • Supports confidential computing options for protecting data in use.
  • Lets a pod have its own kernel settings without touching the host.

Disadvantages

  • Slower start than a regular container because a VM must boot.
  • Fixed memory and CPU cost for every pod.
  • Needs hardware virtualization, which can be complicated on cloud instances.
  • Storage and network crossing the VM boundary can reduce throughput.
  • More moving parts to install, upgrade and keep patched.

The three-way tension

You can think of the decision as a triangle with three corners: security, performance and compatibility. Regular containers sit at performance and compatibility. gVisor leans to security and performance, at some cost to compatibility. Kata leans to security and compatibility, at some cost to performance and density. No point inside the triangle gives you all three corners at once, and anyone who promises otherwise is selling something.

i
Good to Know

Trade-offs shift over time. Both projects keep improving their speed, and hardware gets better at virtualization. Re-measure your own workload every year or so rather than relying on a number you read once.

11Security Deep Dive: What You Gain and What You Do Not

Security is not a switch. It is a stack of layers, and a sandbox is only one of them. Here is how to think about it clearly.

Start with a threat model

A threat model is simply a written answer to three questions: who might attack, what are they after, and how could they try. For sandboxed runtimes, the typical attacker is a tenant or a piece of untrusted code that already runs inside your cluster. Their goals are usually to reach the host, to read other tenants’ data, to steal credentials, or to use your machines for mining and attacks.

A sandbox mainly addresses the first goal and, by extension, the second. It makes the jump from “I control this container” to “I control this node” far harder. That jump is called a container escape, and stopping it is the main reason these runtimes exist.

Layers that work alongside the sandbox

Layer

Least privilege inside the pod

Run as a non-root user, drop extra capabilities, and use a read-only root file system. A hacked app with little power is a small problem.

Layer

Pod security rules

Use Kubernetes Pod Security Standards to forbid privileged pods, host namespaces and risky volume types in sensitive namespaces.

Layer

Network policy

Allow only the connections each workload truly needs. Block access to internal services and cloud metadata endpoints by default.

Layer

Identity and secrets

Give each workload narrow, short-lived credentials. Avoid mounting service account tokens in pods that do not need them.

Layer

Image hygiene

Scan images, pin versions and pull from trusted registries so that a bad image never starts in the first place.

Layer

Patching

Keep the sandbox runtime, the hypervisor, the node operating system and Kubernetes itself up to date. A sandbox with old code is a weaker sandbox.

What a sandbox does not cover

  • Application bugs. A sandbox does not stop someone exploiting your web app. It only limits what they can do next.
  • Over-broad permissions. If the pod carries a powerful cloud role, the attacker uses that role from inside the sandbox, with no escape needed.
  • Shared mounts. Mounting sensitive host directories into the pod hands the attacker a path out, and it is often unsupported anyway.
  • Network trust. A compromised pod can still call services reachable on the network. Isolation of the kernel is not isolation of traffic.
  • Hardware side channels. Attacks that infer secrets from shared processor behavior are a separate problem. VMs and sandboxes help in different ways, but neither erases the risk fully.
  • Supply chain. A poisoned dependency running inside a sandbox is still poisoned. The sandbox just keeps the blast radius smaller.
Everyday Analogy

Locking a thief inside a cell does not help if you also hand them the keys to the building through the food slot. The cell, which is the sandbox, only works if what you pass in and out of it, such as credentials, mounts and network access, is also carefully limited.

Real escape history and the sandbox lesson

Recall the bugs from the first chapter. Flaws that live in the host kernel or in the container runner are reachable by a normal container, because the container shares them. In a sandbox, the application is aimed at a different target first, either the Sentry or a guest kernel. An attacker needs to find a flaw in that new target and then another one past it. This does not make an escape impossible, and both projects have had their own security fixes over the years. What it does is raise the price and force an attacker to chain several bugs together, which is much rarer and much noisier.

!
Watch Out

Never treat a sandbox as a reason to relax the other layers. Teams that think “we have gVisor, so we can run it as root with full permissions” have thrown away the very depth that makes the design work.

12Performance, Scalability and Cost

A stronger wall always costs something. The job of an architect is to know where the cost lands and how to keep it small.

Under 125 ms
Firecracker’s published microVM boot goal
About 5 MiB
Firecracker’s published memory overhead goal per microVM
1 pod, 1 VM
how Kata maps Kubernetes pods to isolation

The figures above come from the design goals that the Firecracker project publishes for its own microVMs. Treat them as best-case examples of how small a VM can be made, not as promises for every Kata setup. Your numbers will depend on the hypervisor, the guest kernel, the image and the hardware.

Where the cost appears

  • Start-up latency. Regular containers start in tens of milliseconds. gVisor adds a little. Kata adds the time to boot a guest, which can reach a few hundred milliseconds or more depending on settings. For long-running services this is irrelevant. For functions that must start on every request, it matters a lot.
  • Per-pod memory. Kata’s VM has a fixed footprint, so a node holds fewer pods than it would with regular containers. gVisor’s footprint is lighter.
  • System call and I/O speed. gVisor’s detour through the Sentry affects call-heavy workloads such as databases that do many small reads. Kata’s cost shows up in shared file systems and virtual network paths.
  • CPU-bound work. Pure computation, such as encoding or number crunching, runs at close to native speed in both.

Ways to keep the cost down

1

Sandbox only what needs it

Do not blanket the whole cluster. Put the risky, untrusted workloads in the sandbox pool and leave trusted services on the fast path.

2

Use dedicated node pools

Group sandbox nodes together, tune them for the runtime, and let the autoscaler grow or shrink that group independently.

3

Account for overhead

Set the RuntimeClass overhead honestly so that the scheduler packs nodes correctly and capacity planning stays truthful.

4

Warm things up

For latency-sensitive tasks, keep a small pool of ready sandboxes, or use snapshot and restore features that the ecosystem offers, so users do not wait for a cold boot.

5

Measure with your real workload

Benchmark your own application on regular, gVisor and Kata runtimes. A generic benchmark tells you little about your database or your API.

Scaling to many thousands of sandboxes

At large scale, the limiting factors are usually node memory for Kata and system-call throughput for gVisor. Platforms that run huge numbers of short tasks tune the guest image to be tiny, trim the number of devices exposed to each VM, and place the sandbox pool in its own autoscaling group. They also watch the rate at which new sandboxes are created, since mass start-ups can saturate the container runtime and the image pull path before the sandbox layer itself.

13Monitoring, Logging and Troubleshooting

A sandbox hides your workload from the host, and that includes hiding it from some of your usual tools. Plan your observability before the first incident, not during it.

What keeps working

The good news is that the everyday Kubernetes experience is unchanged. Application logs written to standard output still flow through the kubelet, so your log shippers keep collecting them. Commands such as viewing logs or opening a shell in a pod still work. Resource requests, limits, readiness checks and liveness checks behave as expected. Metrics for CPU and memory still reach your monitoring system through the normal channels.

What changes

Blind spot

Host-level tracing

Tools that watch system calls on the host, including many security agents built on eBPF, see the sandbox as a few host calls, not the inner activity of your application. The detailed calls happen inside the Sentry or the guest kernel.

Blind spot

Process view

Looking at the host process list does not reveal the processes inside a Kata guest. They live inside a virtual machine, which appears as just a few host processes.

New source

Runtime logs

Both runtimes have their own logs. gVisor can write debug logs for the sandbox, and Kata components log through the node’s system journal, with levels that can be raised during an investigation.

New source

Runtime metrics

Kata ships a monitoring helper that exposes sandbox metrics in a format Prometheus can scrape, and gVisor offers its own metrics server for sandbox statistics.

Putting security monitoring in the right place

If your security team relies on agents that watch the host kernel, they will need a plan for sandboxed pods. One common choice is to run a lightweight agent inside the sandbox, as a sidecar or part of the image, where it sees the sandboxed system calls. Another is to rely on network-level monitoring and audit logs from the cluster API, which are unaffected by the sandbox. Whichever you choose, test it on a sandbox node before declaring the detection coverage complete.

A short troubleshooting checklist

1

Pod stuck in creating

Read the pod events. Look for an unknown handler message, which points to a name mismatch, or a failure to start the VM, which points to missing virtualization support.

2

Pod runs but behaves oddly

Suspect a missing system call or file feature under gVisor. Enable the runtime’s debug logging and look for messages about unsupported calls.

3

Pod is slow

Compare the same workload on a regular runtime. If only the sandbox is slow, check whether it is system-call-heavy, file-heavy, or limited by a small VM size.

4

Pod killed for memory

Confirm that the overhead is set and the limits leave room for the guest kernel and agent. A VM that is too small can run out of memory even when the app seems fine.

5

Not sure the sandbox is active

Check from inside the pod. The kernel information reported by the sandbox differs from the host’s, and that difference confirms which runtime is actually in use.

14Design Patterns and Anti-Patterns

Teams that run sandboxes well tend to follow the same few patterns. Teams that struggle tend to fall into the same few traps.

Patterns that work

Pattern

Dedicated sandbox node pool

Install the runtime only on a labeled and tainted group of nodes. Only pods that ask for the sandbox land there, which keeps costs and complexity contained.

Pattern

Policy-enforced RuntimeClass

Use an admission policy so that every pod in an untrusted namespace is automatically given the sandbox class. Developers cannot forget it or turn it off.

Pattern

One namespace per tenant

Combine the sandbox with a namespace, a network policy and a resource quota per customer. The sandbox protects the kernel, and the rest protects everything above it.

Pattern

Tiered runtimes

Offer regular, gVisor and Kata classes with clear guidance. Teams choose a tier based on how sensitive and how trusted their workload is.

Pattern

Ephemeral sandboxes

For untrusted jobs, create a fresh sandbox per task and destroy it afterward. Nothing an attacker leaves behind survives to the next run.

Pattern

Warm pool

Keep a handful of ready sandboxes for latency-sensitive work so that users do not pay the cold-start price.

Anti-patterns to avoid

ANTI-PATTERN 01Avoid
What it looks like

The team decides to run every workload in a sandbox, including trusted internal services and databases, because “more security is always better.”

Why it hurts

The cluster pays the memory, latency and compatibility costs for workloads that did not need the protection. Databases and message brokers, which are heavy on system calls and storage, often suffer most.

Better approach

Classify workloads by trust level. Sandbox the untrusted ones and apply strong standard hardening to the rest.

ANTI-PATTERN 02Avoid
What it looks like

A pod is run in a sandbox but with elevated settings: broad Linux capabilities, a powerful service account, a mounted host directory, or open access to the cloud metadata service.

Why it hurts

The attacker no longer needs to escape. The pod already holds the keys to important resources, so the sandbox walls protect nothing that matters.

Better approach

Keep least privilege inside the sandbox. Treat the sandbox as an extra layer on top of those settings, never as a replacement for them.

ANTI-PATTERN 03Avoid
What it looks like

The sandbox is rolled out to production without testing the real application, and without setting overhead or node scheduling in the RuntimeClass.

Why it hurts

Hidden compatibility problems appear at the worst moment, and nodes become overcommitted because the scheduler does not know about the sandbox’s own cost.

Better approach

Run a staged rollout. Test in a staging cluster, measure performance, set overhead truthfully and widen the rollout slowly.

15Best Practices and Common Mistakes

This chapter pulls the lessons together into a checklist you can actually use.

Best practices

  • Write down your threat model first. Know who you are defending against. It guides whether you need gVisor, Kata or neither.
  • Test your actual workload. Run your real container, with real traffic patterns, in the sandbox before you commit. Check both correctness and speed.
  • Use RuntimeClass fully. Set the handler, the overhead and the scheduling rules together, so that the platform handles placement and capacity for you.
  • Separate the pools. Keep sandbox nodes apart from regular nodes with labels and taints. This keeps cost visible and troubleshooting simple.
  • Keep all the other layers. Non-root users, limited capabilities, network policies, narrow credentials and image scanning all stay in place.
  • Patch promptly. Track security announcements for your chosen runtime, hypervisor, guest kernel and host operating system. Upgrade on a regular schedule.
  • Plan observability early. Decide how logs, metrics and security detections will work from inside or around the sandbox.
  • Enforce with policy. Use admission controls so that untrusted namespaces cannot run without the sandbox class.
  • Document the limits. Tell developers which features are unsupported, such as privileged mode or host networking, so they do not discover the rules by failure.
  • Rehearse failure. Practice what happens if the sandbox node pool is full, if a runtime upgrade fails, or if a guest crashes.

Common mistakes

  • Believing a sandbox makes an insecure application safe.
  • Choosing a runtime from a blog post benchmark instead of measuring your own workload.
  • Forgetting that Kata on cloud virtual machines needs nested virtualization or bare-metal nodes.
  • Leaving the RuntimeClass overhead empty and then wondering why nodes run out of memory.
  • Mounting host paths or enabling host networking and expecting the sandbox to cope.
  • Using the sandbox for databases or very I/O-heavy systems without checking the performance impact.
  • Skipping upgrades because the sandbox “feels” secure already.
  • Relying only on host-level security agents that cannot see inside the sandbox.
i
Good to Know

A good rule of thumb for a first rollout: pick one clearly untrusted workload, put it in a sandbox in staging for two weeks, compare it with the regular runtime, and only then plan wider adoption.

16Real-World Usage

These ideas are not experimental. Some of the largest cloud platforms run on them every day.

Google Cloud: GKE Sandbox and serverless

gVisor started at Google, and Google Cloud offers it to Kubernetes users as GKE Sandbox, where a node pool can be enabled to run pods under gVisor. Google’s serverless container platform, Cloud Run, has also used gVisor to separate customers’ workloads. The pattern is simple: untrusted customer code, many tenants and a strong boundary at low overhead.

Amazon: Lambda and Fargate on Firecracker

Amazon built Firecracker, a minimal virtual machine monitor, to run AWS Lambda functions and Fargate tasks inside lightweight VMs. Firecracker is not Kata itself, but it follows the same philosophy of one small VM per workload, and Kata Containers can use Firecracker as one of its hypervisors. It shows that the micro VM approach scales to enormous numbers of short tasks.

Microsoft Azure: Pod Sandboxing on AKS

Azure Kubernetes Service offers a pod sandboxing feature built on Kata Containers, so that sensitive pods run in their own isolated VM on shared nodes. Microsoft has also built confidential container options on top of this approach for workloads that need protection of data while it is in use.

Ant Group and Alibaba Cloud: Kata at scale

Companies in the Alibaba ecosystem were among the early drivers of Kata Containers and have publicly discussed running sandboxed containers in production at very large scale, including for shared-tenant cloud services. Their work helped shape the performance improvements and the Rust-based runtime in the project.

Code execution and CI platforms

Platforms that run user code, such as online notebooks, coding challenge sites, build systems and AI code interpreters, commonly place each job in a gVisor or Kata sandbox. The job is short-lived, the code is unknown, and the sandbox is destroyed afterward, which is exactly the situation these runtimes were designed for.

17Frequently Asked Questions

Quick answers to the questions that come up most often.

Q1Do I need to change my container images to use gVisor or Kata?
No. Images built for regular containers work as they are. The change is on the node and in the RuntimeClass. The only caveat is gVisor’s compatibility gaps, which you can only find by testing.
Q2Is a sandbox the same as a virtual machine?
Not always. Kata uses a real virtual machine per pod. gVisor does not create a VM in the traditional sense. It is a user-space program that acts as a kernel for the container, so it is lighter and has a different set of trade-offs.
Q3Which is more secure, gVisor or Kata?
Both greatly improve on regular containers, and the better answer depends on your threat model. Kata relies on hardware virtualization and a separate guest kernel. gVisor relies on a narrow, memory-safe interception layer. Security teams often choose Kata for the strictest boundary and gVisor for efficiency at scale.
Q4Can I run both in the same cluster?
Yes. Create one RuntimeClass per runtime, install each on its own node pool and let teams choose. This is a very common setup.
Q5Will my application be much slower?
For CPU-bound work, usually not. For workloads with heavy system call, file or network traffic, the slowdown can be noticeable, especially under gVisor. Measure your own workload before deciding.
Q6Can a sandboxed pod run privileged or use the host network?
Generally no, or only in limited form. Those settings exist to remove isolation, which contradicts the purpose of a sandbox. Redesign the workload rather than trying to force these settings.
Q7Does Kata work on any cloud virtual machine?
Only if the machine allows nested virtualization or you use bare-metal instances. Some cloud instance types do not expose the needed processor features, so check your provider’s documentation first.
Q8What about GPUs and special hardware?
Support exists but is narrower than for regular containers and depends on the runtime version and hardware. Verify your specific accelerator and driver combination before relying on it.
Q9Do sandboxes replace Pod Security Standards and network policies?
No. They add to them. Sandboxes defend the kernel boundary, while pod security rules and network policies defend permissions and traffic. You want all of them together.
Q10Is there a managed option so I do not have to install anything?
Yes. Several managed Kubernetes services, such as GKE and AKS, offer sandboxing as a built-in feature you switch on for a node pool. Self-managed clusters need to install and maintain the runtime themselves.
Q11How do I know the pod is really sandboxed?
Check the pod’s runtime class, confirm it landed on a sandbox node, and inspect the kernel information from inside the pod. A sandboxed pod reports a different kernel identity from the host’s.
Q12Should every cluster use a sandboxed runtime?
No. If you run only trusted code you wrote yourself, regular containers with strong hardening are often enough. Sandboxes shine when you run code from others or when your risk level or regulations demand a stronger boundary.

18Summary and Key Takeaways

What to remember

  • Containers share one kernel. That shared kernel is their biggest security weakness when the code inside is not fully trusted.
  • A sandboxed runtime adds a second wall. It replaces runc deep in the stack, so Kubernetes, images and tooling keep working as before.
  • gVisor intercepts and re-implements. Its Go-based Sentry answers most system calls itself, and a Gofer and a strict filter keep its reach to the host very small.
  • Kata virtualizes. Each pod gets its own lightweight VM and guest kernel, with hardware isolation and excellent Linux compatibility, at the cost of start-up time and memory.
  • RuntimeClass is the switch. Use its handler, overhead and scheduling fields together, and pair it with dedicated, tainted node pools.
  • Sandboxes complement, not replace, other defenses. Keep least privilege, network policies, narrow credentials, image hygiene and fast patching.
  • Choose by threat model and measurement. Sandbox the untrusted workloads, test your real application, and re-measure over time rather than trusting a single benchmark.