Running Wasm Workloads in Kubernetes with SpinKube Patterns
How SpinKube's containerd-shim-spin, runwasi, and runtime-class-manager fit into Kubernetes — plus install, node labeling, and scheduling patterns for running Spin apps beside containers.
Published on • November 15, 2026
AI Assistant

Kubernetes never actually required containers — it required a runtime interface. That distinction is what makes SpinKube possible: a CNCF sandbox project (accepted January 21, 2025) that runs serverless WebAssembly applications on ordinary clusters by plugging the Wasm execution path into containerd’s shim mechanism. No forked kubelet, no custom scheduler, no parallel control plane: Spin apps become Pods with a different runtimeClassName. This post walks the full architecture — containerd-wasm-shim lineage, runwasi, spin-operator, and runtime-class-manager — then shows the install sequence, node labeling, and the scheduling patterns you need to run Wasm workloads safely alongside containers.
Architecture: the shim is the integration point
To understand SpinKube you need one piece of Kubernetes trivia: the kubelet talks to containerd over the CRI, and containerd delegates each container to a small helper process — a containerd shim — which in turn calls a low-level runtime. The default chain is containerd → containerd-shim-runc-v2 → runc. Kubernetes’ RuntimeClass mechanism lets you swap the runtime per Pod, which means you can swap the shim.
SpinKube exploits exactly that seam:
kubelet ──CRI──▶ containerd ──▶ containerd-shim-spin ──▶ Spin runtime
│
└── built on runwasi (library)
The three projects that compose SpinKube:
- containerd-shim-spin — a containerd shim, built on the runwasi library (the CNCF project that provides shared plumbing for writing Wasm shims), that executes Spin applications. It loads the Wasm module, pre-compiles it for the node’s CPU architecture (caching the result in containerd’s content store), and starts serving — with sub-millisecond startup regardless of idle time.
- spin-operator — a Kubernetes operator (kubebuilder-based) that owns the SpinApp and SpinAppExecutor custom resources. It realizes SpinApps as ordinary Deployments/ReplicaSets/Pods and wires executors to runtime classes.
- runtime-class-manager (formerly KWasm) — the node lifecycle manager. It installs and version-pins the shim binary on nodes, manages the corresponding
RuntimeClass, and handles node labels — replacing the fragile “curl a binary onto every worker” bootstrap.
Because Spin apps ship as OCI artifacts rather than full container images, the node pulls only the compiled module and its assets: no base image, no package manager, no web-server packages to patch. Security patching of the application boundary moves to the host runtime.
Installing SpinKube
The install has four layers — CRDs, runtime-class-manager, the operator, and the executor — and works the same on AKS, MicroK8s, or any cluster with containerd. A representative sequence (versions from the SpinKube docs):
# 1. Spin operator CRDs + RuntimeClass for Spin
kubectl apply -f github.com/spinframework/spin-operator/releases/download/v0.6.1/spin-operator.crds.yaml
kubectl apply -f github.com/spinframework/spin-operator/releases/download/v0.6.1/spin-operator.runtime-class.yaml
# 2. Runtime Class Manager — installs shims onto nodes
helm upgrade --install runtime-class-manager \
--namespace runtime-class-manager --create-namespace \
--version 0.2.0 \
oci://ghcr.io/spinframework/charts/runtime-class-manager
# 3. A Shim resource that says which shim to install where
kubectl apply -f github.com/spinframework/containerd-shim-spin/releases/download/v0.27.1/runtime-class-manager-shim-v1alpha1-v0.27.1.yaml
# 4. The operator itself
helm upgrade --install spin-operator \
--namespace spin-operator --create-namespace \
--version 0.6.1 --wait \
oci://ghcr.io/spinframework/charts/spin-operator
# 5. The shim executor CRD that ties executors to runtime classes
kubectl apply -f github.com/spinframework/spin-operator/releases/download/v0.6.1/spin-operator.shim-executor.yaml
On managed platforms this is faster: Rancher Desktop ships checkboxes for wasm support and the Spin operator, and AKS docs compress the flow into cert-manager plus two kubectl applys and one Helm install.
Node labels, taints, and where Wasm runs
A cluster with the shim installed can run Spin apps on any node it labels. The Shim resource carries a nodeSelector (the docs’ example matches spin=true), and runtime-class-manager applies the label to nodes as it installs the shim:
# Label the nodes that should accept Spin workloads
kubectl label node --all spin=true
For a mixed fleet — Wasm-optimized edge nodes alongside GPU or high-memory container nodes — the standard Kubernetes isolation idioms apply unchanged:
# Dedicated Wasm pool: taint the nodes...
kubectl taint nodes wasm-pool dedicated=wasm:NoSchedule
# ...and label them for selection
kubectl label node wasm-pool-0 spin=true wasm-pool=true
Then a SpinApp (or any Pod) using the Spin runtime class only schedules onto that pool via the shim’s nodeSelector, tolerations, or an explicit nodeSelector in the workload spec. Nothing about this is Wasm-specific: it is the same taint/toleration/label pattern you already use for GPU nodes. The one invariant to respect: a Pod’s runtimeClassName must match a runtime installed on whichever node the scheduler picks, or the kubelet will fail the Pod at start — runtime-class-manager exists to make that invariant a non-issue.
Deploying Spin apps
With everything installed, deployment is a custom resource. You can write it by hand, scaffold it with the Spin CLI plugin, or generate manifests in CI:
spin build
spin registry push ttl.sh/greeter:24h
spin kube scaffold --from ttl.sh/greeter:24h | kubectl apply -f -
The generated object uses the SpinApp CRD:
apiVersion: core.spinkube.dev/v1alpha1
kind: SpinApp
metadata:
name: spin-greeter
spec:
image: "ttl.sh/greeter:24h"
executor: containerd-shim-spin
replicas: 2
The operator converts it into a standard Deployment, so everything downstream — Services, Endpoints, HPA, NetworkPolicies, ConfigMaps, Secrets, DNS, metrics — behaves exactly as it does for container pods. Recent SpinKube versions also expose Spin apps through the Gateway API using the same routes as containerized services. Liveness and readiness probes work; because a Spin app starts in milliseconds, scale-from-zero reacts almost instantly.
Scheduling Wasm alongside containers: the patterns that work
Running both workload types on one cluster raises practical questions. The patterns that hold up in production:
1. Runtime-class as the workload boundary. Keep runtimeClassName: spin (or the WASI runtime class) explicit in every Wasm workload’s pod spec, and let the operator own it. Mixing container pods and Spin pods in the same Deployment is not supported — one Pod, one runtime.
2. Heterogeneous scheduling without surprises. Use node pools: container workloads get default nodes; Wasm workloads get labeled/tainted pools sized for density (a node that fits 40 container pods might fit 400 Spin replicas). Because SpinKube reuses the scheduler, you can use topology spread constraints, affinity, and PriorityClasses as usual.
3. Resource requests still count. The kubelet enforces cgroup limits regardless of runtime. Set requests/limits on Spin pods — a common early mistake is assuming Wasm’s small footprint makes limits unnecessary, then losing pods to eviction when a burst exceeds the (too-tight) limit or wasting an entire node’s allocatable on (too-generous) requests.
4. Keep stateful services in containers. Spin apps are stateless functions with bindings to external stores (key-value, SQL, Redis). Databases, queues, and legacy services stay in containers — the two runtimes are complementary, and cross-runtime traffic flows over ordinary Services.
5. Patch management through the host. With the shim pinned and versioned by runtime-class-manager, patching the Wasm runtime is a node-pool operation, not an image rebuild — the same argument Docker made for base-image hygiene, moved one layer down.
When SpinKube is the right fit
SpinKube shines for event-driven APIs, webhooks, IoT ingestion, and edge functions — workloads where sub-millisecond start, tiny artifacts, and per-instance sandboxing beat raw sustained throughput. It is not a container replacement: long-running stateful services and OS-dependent workloads remain in the container path. But as a way to run both in one cluster under one control plane, the pattern is now boring in the best sense — a RuntimeClass, a shim, and labels you already know how to manage.
References
- SpinKube project documentation — https://www.spinkube.dev/
- “Introducing SpinKube”, SpinKube blog, March 13, 2024 — https://www.spinkube.dev/blog/2024/03/13/introducing-spinkube
- SpinKube architecture overview — https://www.spinkube.dev/docs/topics/architecture/
- runwasi — containerd shims for Wasm runtimes — https://github.com/containerd/runwasi
- containerd-shim-spin — https://github.com/spinframework/containerd-shim-spin
- runtime-class-manager — https://github.com/spinframework/runtime-class-manager
- Deploy SpinKube to AKS, Microsoft Learn — https://learn.microsoft.com/en-us/azure/aks/deploy-spinkube
- “Exposing Spin apps on SpinKube with GatewayAPI”, CNCF, February 26, 2026 — https://www.cncf.io/blog/2026/02/26/exposing-spin-apps-on-spinkube-with-gatewayapi