DEV Community

Cover image for Running WASM on Kubernetes with SpinKube: A Hands-On Guide
Bhargav Bhatt
Bhargav Bhatt

Posted on Fully Autonomous

Running WASM on Kubernetes with SpinKube: A Hands-On Guide

The Problem With Container Cold Starts

You've felt it. You scale a Deployment from 0 → 1, and for 300ms to 2 seconds, your pod is in ContainerCreating. At 10k concurrent requests, that's not a blip — it's a latency cliff.

WebAssembly solves this at the runtime level: single-digit millisecond cold starts, ~2 MB memory per instance (vs. 100+ MB for a typical container), and a capability-based security model that's sandboxed by default.

The question was never should we run Wasm on Kubernetes. The question was how, without breaking everything we already have.

SpinKube is the answer.


What Is SpinKube?

SpinKube is a CNCF Sandbox project (accepted January 2025) that makes WebAssembly a first-class workload in Kubernetes. It's built on the Spin framework (also CNCF Sandbox) and is written in Rust.

The key insight: no new API, no new scheduler, no parallel platform. SpinKube plugs into the existing containerd → CRI → RuntimeClass path. Your Wasm app gets scheduled with the same kubectl apply, the same HPA, the same service mesh, the same GitOps pipeline.

Fun fact: SpinKube was originally created by Fermyon. When Akamai acquired Fermyon in December 2025, the project was already safely under CNCF governance — a structural guarantee that outlasts any single company's roadmap.

GitHub: github.com/spinframework/spinkube (migrated from the spinkube org during CNCF onboarding)
Stars: 6,200+ · Contributors: 80+ · License: Apache 2.0


Architecture at a Glance

┌─────────────────────────────────────────────────────────┐
│  Your SpinApp YAML                                       │
│  (kubectl apply -f spinapp.yaml)                         │
└──────────────────────┬──────────────────────────────────┘
                       ▼
┌─────────────────────────────────────────────────────────┐
│  spin-operator (Kubebuilder operator)                    │
│  • Watches SpinApp CRD                                   │
│  • Creates Deployment + Service                          │
│  • Sets runtimeClassName: wasmtime-spin-v2               │
└──────────────────────┬──────────────────────────────────┘
                       ▼
┌─────────────────────────────────────────────────────────┐
│  Kubernetes Scheduler                                    │
│  • Standard constraints + RuntimeClass selector          │
│  • Places pod on nodes with the Spin shim installed      │
└──────────────────────┬──────────────────────────────────┘
                       ▼
┌─────────────────────────────────────────────────────────┐
│  containerd                                              │
│  • Sees runtimeClassName → routes to containerd-shim-spin│
│  • Shim launches Wasmtime (or Wasmer/WasmEdge)           │
│  • Wasm module executes — no runc, no OCI container      │
└─────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Three components you actually interact with:

Component Role
spin-operator CRD + controller. Turns SpinApps into Deployments.
containerd-shim-spin The runtime. Replaces runc for Wasm pods. Built on runwasi.
runtime-class-manager Auto-installs and manages the shim across your node pool. Replaces the old KWasm operator.

Prerequisites

  • Kubernetes 1.27+ (k3s, kind, EKS, GKE, AKS all work)
  • containerd as the container runtime (not Docker)
  • Spin CLI installed locally
  • A Wasm-compatible language toolchain (Rust, Go, JavaScript, Python, C#)

Step 1: Install SpinKube on Your Cluster

For a quick test on kind or k3s:

# Install the Runtime Class Manager (manages shim lifecycle)
helm repo add spinkube https://spinkube.github.io/charts
helm install runtime-class-manager spinkube/runtime-class-manager \
  --namespace spinkube-system --create-namespace

# Install the Spin Operator
helm install spin-operator spinkube/spin-operator \
  --namespace spinkube-system
Enter fullscreen mode Exit fullscreen mode

The Runtime Class Manager will:

  1. Label nodes that support the Spin runtime
  2. Install containerd-shim-spin on those nodes
  3. Create the RuntimeClass resource (wasmtime-spin-v2)

Verify:

kubectl get runtimeclass
# NAME              HANDLER
# wasmtime-spin-v2  spin

kubectl get nodes -l spinkube.io/runtime=wasmtime-spin-v2
Enter fullscreen mode Exit fullscreen mode

Step 2: Build a Spin App

Create a new Spin project (Rust example):

spin new my-wasm-app --language rust
cd my-wasm-app
Enter fullscreen mode Exit fullscreen mode

spin-new generates a minimal HTTP handler. Edit src/main.rs:

use spin_sdk::http::{Request, Response};

#[http_component]
pub fn handle(req: Request) -> Response {
    let name = req.uri().path().trim_start_matches('/');
    Response::ok(format!("Hello, {name}! (served by Wasm on K8s)"))
}
Enter fullscreen mode Exit fullscreen mode

Build and package:

spin build
# Produces: target/wasm32-wasi/release/my-wasm-app.wasm
Enter fullscreen mode Exit fullscreen mode

The output is a ~2 MB Wasm binary — compared to a ~100 MB+ OCI image for an equivalent Rust HTTP server.


Step 3: Deploy to Kubernetes

Create spinapp.yaml:

apiVersion: spinkube.dev/v1alpha1
kind: SpinApp
metadata:
  name: my-wasm-app
  namespace: default
spec:
  replicas: 3
  image: ghcr.io/yourorg/my-wasm-app:latest  # Push your Spin app to any OCI registry
  runtime: wasmtime-spin-v2
  port: 80
  resources:
    requests:
      memory: "4Mi"
      cpu: "10m"
    limits:
      memory: "32Mi"
      cpu: "100m"
Enter fullscreen mode Exit fullscreen mode

Apply:

kubectl apply -f spinapp.yaml
Enter fullscreen mode Exit fullscreen mode

The spin-operator reconciles this into a standard Deployment with runtimeClassName: wasmtime-spin-v2. Watch it go:

kubectl get pods -w
# my-wasm-app-7f8b4c6d2-x1k9p   1/1   Running   0   3s
# my-wasm-app-7f8b4c6d2-m2n8q   1/1   Running   0   3s
# my-wasm-app-7f8b4c6d2-p3o7r   1/1   Running   0   3s
Enter fullscreen mode Exit fullscreen mode

~3 seconds from kubectl apply to all pods Running. Compare that to a typical containerized app pulling a 150 MB image.


Step 4: Verify It Works

Create a Service and hit it:

kubectl expose deployment my-wasm-app --port=80 --type=NodePort
kubectl get svc my-wasm-app -o wide
Enter fullscreen mode Exit fullscreen mode

Then:

curl http://<node-ip>:<port>/world
# Hello, world! (served by Wasm on K8s)
Enter fullscreen mode Exit fullscreen mode

Step 5: Scale It (The Fun Part)

kubectl scale spinapp my-wasm-app --replicas=100
Enter fullscreen mode Exit fullscreen mode

100 Wasm instances. Total memory: ~200 MB (vs. ~10 GB for 100 containerized instances of the same app).

Add an HPA for autoscaling:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: my-wasm-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: my-wasm-app
  minReplicas: 3
  maxReplicas: 200
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
Enter fullscreen mode Exit fullscreen mode

Or use KEDA for event-driven scaling (queue depth, HTTP request rate). SpinKube integrates with KEDA out of the box.


Step 6: Observe It

SpinKube exports metrics to OpenTelemetry. Point your OTel collector at the pods and you get traces in Jaeger, metrics in Prometheus — same as any other K8s workload.

As of early 2026, SpinKube also supports Gateway API for ingress, so your Wasm apps sit behind the same API Gateway as your containerized services.


Containers vs. Wasm on SpinKube: The Honest Comparison

Dimension Container (runc) Wasm (SpinKube)
Cold start 100 ms – 2 s 1–10 ms
Idle memory 50–500 MB 2–30 MB
Artifact size 100 MB – 2 GB 1–10 MB
Security model Namespace + cgroups (OS-level) Capability-based (WASI) — no file/network access unless explicitly granted
Full OS access ✅ ❌ (no raw sockets, no threads in WASI 0.2)
Scheduling Standard Identical (same kubectl apply)
Best for Stateful, long-lived, OS-dependent services Stateless, I/O-light, high-frequency handlers

They coexist. Run your Postgres and your message queue as containers. Run your API gateway, request transformers, and edge functions as Wasm. Same cluster, same namespace, same tooling.


When SpinKube Makes Sense (and When It Doesn't)

✅ Use SpinKube when:

  • You have short-lived, stateless HTTP handlers (edge functions, API transforms, auth checks)
  • Cold start latency is a real problem (serverless patterns, bursty traffic)
  • You want to pack more workloads per node (density = cost savings)
  • You need a smaller attack surface (WASI capability model)

❌ Skip SpinKube when:

  • Your workload needs full OS access (raw sockets, FUSE, GPU drivers)
  • It's a long-lived stateful service (database, cache)
  • Your language/toolchain can't target wasm32-wasi
  • You're in a Docker-only environment (SpinKube requires containerd)

What's Missing (and Where Funding Helps)

SpinKube is in CNCF Sandbox — the earliest maturity level. That means:

  • 13 maintainers (6 from Fermyon/Akamai, 3 from Microsoft, 1 each from SUSE, Liquid Reply, Hetzner, independent) — a small team for a project this central to the Wasm-on-K8s story.
  • Roadmap items still open: automatic OpenTelemetry instrumentation for Wasm apps, more mature multi-runtime support, Windows node support, and a formal security audit.
  • Docs and tutorials are good but thin compared to, say, Helm or KEDA. The community needs more "I built X with SpinKube" content.
  • No LFX Crowdfunding campaign is active yet. The project could use funding for: CI infrastructure, conference travel (KubeCon, Wasm I/O), a part-time community manager, and a security audit.

If you found this guide useful and want to see SpinKube move to Incubating, the most impactful things you can do:

  1. Contribute — even docs, bug reports, and test cases count. GitHub
  2. Use it in production and share your experience (a Dev.to post, a talk, a blog).
  3. Support CNCF funding efforts — when the project launches an LFX Crowdfunding campaign, it'll be listed on the CNCF funding page.
  4. Join the community — SpinKube Slack (via CNCF Slack) is where decisions happen.

TL;DR

Fact Detail
What CNCF Sandbox project for running Wasm on Kubernetes
How containerd shim + RuntimeClass — no new API
Cold start 1–10 ms
Memory ~2 MB per instance
Languages Rust, Go, JS, Python, C# (anything that targets wasm32-wasi)
Runtimes Wasmtime (default), Wasmer, WasmEdge
Maturity Sandbox (accepted Jan 2025)
GitHub spinframework/spinkube
License Apache 2.0

What's your experience with Wasm on Kubernetes? Have you tried SpinKube in production? Drop your setup in the comments — I'm especially curious about anyone running it alongside KEDA or Gateway API.

If you want to support the project, the best thing you can do right now is **contribute* or write about your experience. The CNCF funding infrastructure is there and ready when the project is ready to use it.*

Top comments (0)