The useful question about WebAssembly on Kubernetes is not “can it replace containers?” It is “can we give the few workloads that benefit from Wasm a safe lane without creating a second application platform to operate?” For an existing Cluster API fleet, SpinKube makes the answer unusually concrete: add a supported runtime to selected nodes, expose it through a Kubernetes RuntimeClass, and schedule only compatible workloads onto that lane.
That is deliberately less glamorous than a rewrite. It is also more useful. A team can preserve its cluster API, nodes, registries, GitOps flow, ingress, service discovery, observability, and ordinary container deployments. Spin workloads enter through the same Kubernetes control loop, while the runtime behind selected pods changes. The important qualification is that “one more RuntimeClass” is an API-level description, not a claim that the node work disappears.
The answer: a new execution lane, not a new fleet
Here is the operational delta for a Cluster API-managed Kubernetes fleet. The core distinction is between new node runtime support and unchanged platform machinery.
| Layer | Existing container lane | SpinKube lane | What the operator actually changes |
|---|---|---|---|
| Machines and node pools | Cluster API creates, replaces, and upgrades nodes | The same Cluster API reconciliation remains in charge | Roll out the shim only in a labelled worker pool or a new machine deployment; do not hand-configure live nodes. |
| Node runtime | containerd runs the usual OCI containers | containerd can invoke containerd-shim-spin for Spin workloads | Install the shim and add the spin runtime handler to containerd; manage both as image/bootstrap lifecycle inputs. |
| Kubernetes scheduling | Scheduler places pods by ordinary constraints | Scheduler places pods by the same constraints plus RuntimeClass placement rules | Create one RuntimeClass whose handler is spin and whose selector targets shim-equipped nodes. |
| Deployment interface | Deployment, Helm chart, GitOps manifest | The same object types can carry runtimeClassName | Set runtimeClassName in the Pod template; a chart needs only to pass through that standard PodSpec field. |
| Networking and telemetry | Service, Gateway/Ingress, DNS, metrics and logs | The same Kubernetes surfaces | Validate the app’s HTTP path and telemetry, not a second scheduler or service mesh. |
| Developer abstraction | Plain OCI workload or a platform’s deploy API | Plain Deployment or the optional Spin Operator/SpinApp CRD | Choose the operator only if its authoring workflow is useful; it is not needed to make the runtime work. |
The upstream shim project describes the same basic sequence: install its binary on the nodes, configure containerd to recognize it, then apply a RuntimeClass whose handler matches that runtime name. It explicitly says the shim is additive: nodes can run Spin applications alongside Linux containers. The containerd-shim-spin documentation also recommends a node selector so a pod cannot land on a node without the shim.
This is why SpinKube is attractive for a narrow workload class. RuntimeClass does not introduce a scheduler, a new cluster, or a parallel network plane. Kubernetes still admits, schedules, restarts, and exposes the pod. It simply asks containerd to use a different handler once the pod reaches an eligible node.
Follow the request from a Deployment to the Spin runtime
The stack is worth spelling out because it keeps the boundary honest:
- A developer publishes a Spin application as an OCI artifact and declares a normal Kubernetes workload.
- The Pod template names a RuntimeClass, for example
wasmtime-spin-v2. - Kubernetes maps that RuntimeClass to the
spinhandler and applies its scheduling rules. - On the selected node, containerd maps
spintoio.containerd.spin.v2and startscontainerd-shim-spin-v2. - The shim runs the application through the Spin runtime. Its lower-level foundation is runwasi, which exists specifically to run Wasm workloads under containerd directly or through kubelet’s CRI integration.
The result remains a Pod from Kubernetes’ point of view. That is the practical meaning of “same kubectl and Helm,” not a claim that every container image can suddenly run as Wasm. A RuntimeClass is a runtime selector; it is not an automatic compilation step, a compatibility layer for arbitrary Linux packages, or a capacity planner.
SpinKube can also supply an optional authoring layer. Its Spin Operator watches SpinApp custom resources and realizes their desired state in the cluster. That is useful when a platform wants to give developers a Spin-native deployment contract. But the underlying shim README also shows a regular apps/v1 Deployment using runtimeClassName, which is the lighter path for a platform that already has a deploy controller and does not want another tenant-facing object model.
The real node change: make it a fleet rollout, not a shell session
For containerd 2+, the shim documentation gives this runtime-handler shape:
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin]
runtime_type = "io.containerd.spin.v2"
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.spin.options]
SystemdCgroup = trueThe binary must also be present on every node that advertises the lane. Those two facts are the non-negotiable operations work. In a Cluster API fleet, treat them as versioned machine configuration: bake them into the immutable node image or deliver them through the approved bootstrap mechanism; create or update a labelled MachineDeployment; and let CAPI replace nodes under its normal health and rollout controls. A manual SSH edit is both unreproducible and likely to vanish at the next replacement.
Production installs do not need to hand-roll those steps. SpinKube’s current quickstart says to use the Runtime Class Manager in production to install and manage the shim lifecycle, replacing the older kwasm operator. Its project documentation describes a Kubernetes operator that creates RuntimeClasses and installs/configures containerd shims from a Shim custom resource. That reduces repetitive node work, but it does not remove the need to test the manager against the fleet’s node OS, containerd version, and upgrade workflow.
Once the node pool is ready, the Kubernetes-facing contract can be small and reviewable:
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: wasmtime-spin-v2
handler: spin
scheduling:
nodeSelector:
wasm.bex.co/spin: "true"apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-spin
spec:
replicas: 2
selector:
matchLabels:
app: hello-spin
template:
metadata:
labels:
app: hello-spin
spec:
runtimeClassName: wasmtime-spin-v2
containers:
- name: app
image: ghcr.io/spinkube/containerd-shim-spin/examples/spin-rust-hello:v0.25.1
command: ["/"]The exact image tag should be pinned to the shim release approved for the fleet; the shim repository publishes a release-to-Spin version map. The nodeSelector is not decoration. Without it, a pod can be scheduled to an ordinary node where the spin handler does not exist.
Helm does not need a Spin-specific install path to use this pattern. A chart that exposes PodSpec fields can render spec.template.spec.runtimeClassName: wasmtime-spin-v2; a platform chart that does not expose the field needs a small, explicit template/value change. Either way, admission, rollout history, kubectl describe, Services, Gateway API routes, and existing GitOps diffs stay familiar.
Decide whether you need the Spin Operator, not whether you need a second platform
There are two legitimate interfaces, and they answer different needs.
| If the team needs… | Start with… | Why |
|---|---|---|
| An existing platform controller already rendering Deployments | RuntimeClass plus ordinary Deployments | It adds only a runtime choice to the current contract. |
A Spin-native build/deploy experience and a controller that understands SpinApp | Spin Operator | It turns Spin application intent into Kubernetes resources. |
| A broad distributed component model, runtime hosts, capability providers, and lattice networking | Evaluate wasmCloud separately | That is a larger application-runtime architecture, not merely a shim on an existing node pool. |
This is where comparisons often become misleading. wasmCloud on Kubernetes uses an operator and CRDs to run wasmCloud hosts, and its workloads can span a lattice that includes hosts outside Kubernetes. Its own documentation frames Kubernetes as infrastructure and wasmCloud as the application layer. That can be the right model for a component-oriented, distributed Wasm system. It is not the minimum dependency required to run a Spin HTTP workload on a Kubernetes node.
SpinKube’s minimal lane therefore should not be sold as “better than” wasmCloud. It makes a narrower trade: preserve the container-oriented control plane and add a runtime class for a framework-compatible workload. If the application needs wasmCloud’s cross-host component model, its extra control-plane concepts are part of the value, not accidental complexity.
Where “one more RuntimeClass” stops being the whole story
The simple path works only when the workload fits Spin and the operations are treated as real runtime operations.
- Application fit: Spin/WASI workloads must be built for the runtime and its capability model. Existing stateful services, software needing a full Linux userland, native extensions, privileged access, or complex OS dependencies should remain containers unless they are deliberately redesigned.
- Version discipline: pin and test the shim, Spin runtime, containerd, and node image as a set. The published shim-to-Spin compatibility map is a release-management input, not a suggestion.
- Security discipline: a Wasm sandbox is not permission to stop patching. For example, runwasi published a June 2026 critical advisory for its Wasmtime shim’s precompile-cache path, patched in v0.6.1. The advisory is a useful reminder to track the runtime supply chain and its security releases like any other node component.
- Platform economics: a separate lane earns its keep only when a measured need exists: very fast startup, a constrained code-execution boundary, or a compatible lightweight HTTP/event workload. “It is Wasm” is not a capacity model.
Start with a reversible pilot: one labelled worker pool, one stateless HTTP Spin app, the same Service and Gateway path used by containers, and dashboards for pod readiness, errors, latency, node failures, and image/shim versions. Exercise a CAPI node replacement while the app is deployed. Roll back by stopping new RuntimeClass workloads and draining that pool; the rest of the fleet is still a standard containerd Kubernetes fleet.
That pilot produces the data a platform actually needs. If it confirms a real startup-time or isolation advantage for a supported workload, expand the lane. If it mainly exposes an incompatible application model, leave the container path alone. The win is not pretending WebAssembly has displaced Kubernetes containers. It is giving the workloads that fit a new runtime a Kubernetes-native place to run.
Bex.co is the open-source, AI-native Render alternative—push a Git repo and get a running HTTPS service on machines you own. For platform teams, an opt-in runtime lane is a useful pattern: keep ordinary deploys simple while making infrastructure state explicit enough for operators and agents to manage.



