← All posts

Five container runtimes: when runc is not the answer

runc, gVisor, Kata, Firecracker, and WASM on one Go service — cold start, memory, and threat-model fit.

Contents

In brief

AWS Lambda does not run your code in a classic Docker container — it uses Firecracker microVMs. Multi-tenant GKE leans on gVisor; Cloudflare Workers on WASM. On Dev.to, the author ran the same 3 MB Go HTTP server through five isolation layers and compared cold start, memory, and where each runtime fits.

What happened

The baseline is distroless + runc: a minimal image with no shell or package manager. Isolation still shares the host kernel, but post-exploit tooling disappears — you are not stopping the breach, you are starving it.

gVisor (runsc) intercepts syscalls in the user-space Sentry kernel; the host does not see container syscalls directly. Enable with --runtime=runsc. Systrap mode (since 2023) is much faster than early gVisor. Typical fits: CI runners on untrusted code, SaaS plugins, cloud IDEs.

Kata Containers with QEMU boots a lightweight VM per container — a real kernel boundary. Cold start ~500 ms, memory overhead ~52 MB. Needed when audits demand VM-level isolation (PCI-DSS, HIPAA, multi-tenant databases).

Kata + Firecracker uses the same OCI image with a slimmer VMM: ~125 ms cold start, ~28 MB overhead. That is the substrate behind Lambda, Fly.io Machines, burst LLM inference, and PR preview environments.

WASM/WASI is a separate compile target with capability-based isolation and no syscalls by default. HTTP in WASI preview1 is still limited, but WASI 0.2 and wasi-http ship on edge platforms. Docker Desktop 4.27+ supports --platform=wasi/wasm.

At steady state, p50 latency stays under 1 ms across OCI runtimes — you pay in cold start and RAM, not throughput.

Runtime Cold start Memory (approx.)
runc / distroless ~20 ms ~7 MB
gVisor ~50 ms ~18 MB
Kata / QEMU ~500 ms ~52 MB
Kata / Firecracker ~125 ms ~28 MB

Why it matters

“Which runtime is more secure?” is the wrong question. Threat models differ: trusted images in your cluster, arbitrary user code, compliance paperwork, or edge scripts each point to a different runtime.

Image size barely moves (~3 MB) — only --runtime changes. That removes the fear that stronger isolation breaks your build pipeline.

In practice

  1. Internal microservices in a single-tenant cluster → runc + distroless hygiene.
  2. CI/CD and SaaS executing user code → gVisor without KVM, syscall isolation.
  3. Compliance language requiring a VM boundary → Kata/QEMU or confidential containers.
  4. Serverless platforms, preview envs, burst jobs → Firecracker.
  5. Edge, plugins, one binary everywhere → WASM/WASI and frameworks like Spin.

The copyleftdev/micro-containers repo ships benchmarks and install.sh per runtime.

Takeaway

The container world is bigger than Docker + runc. Pick for threat model, cold start, and ops cost — not “maximum security.” For interactive platforms, Firecracker often sits between gVisor and full QEMU VMs.