Skip to main content

SpinKube on an Existing Kubernetes Fleet: One More RuntimeClass, Not a Parallel Platform

9 min readDora NodaDora Noda
Share
On this page

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.

LayerExisting container laneSpinKube laneWhat the operator actually changes
Machines and node poolsCluster API creates, replaces, and upgrades nodesThe same Cluster API reconciliation remains in chargeRoll out the shim only in a labelled worker pool or a new machine deployment; do not hand-configure live nodes.
Node runtimecontainerd runs the usual OCI containerscontainerd can invoke containerd-shim-spin for Spin workloadsInstall the shim and add the spin runtime handler to containerd; manage both as image/bootstrap lifecycle inputs.
Kubernetes schedulingScheduler places pods by ordinary constraintsScheduler places pods by the same constraints plus RuntimeClass placement rulesCreate one RuntimeClass whose handler is spin and whose selector targets shim-equipped nodes.
Deployment interfaceDeployment, Helm chart, GitOps manifestThe same object types can carry runtimeClassNameSet runtimeClassName in the Pod template; a chart needs only to pass through that standard PodSpec field.
Networking and telemetryService, Gateway/Ingress, DNS, metrics and logsThe same Kubernetes surfacesValidate the app’s HTTP path and telemetry, not a second scheduler or service mesh.
Developer abstractionPlain OCI workload or a platform’s deploy APIPlain Deployment or the optional Spin Operator/SpinApp CRDChoose 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:

  1. A developer publishes a Spin application as an OCI artifact and declares a normal Kubernetes workload.
  2. The Pod template names a RuntimeClass, for example wasmtime-spin-v2.
  3. Kubernetes maps that RuntimeClass to the spin handler and applies its scheduling rules.
  4. On the selected node, containerd maps spin to io.containerd.spin.v2 and starts containerd-shim-spin-v2.
  5. 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:

toml
[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 = true

The 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:

yaml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: wasmtime-spin-v2
handler: spin
scheduling:
  nodeSelector:
    wasm.bex.co/spin: "true"
yaml
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 DeploymentsRuntimeClass plus ordinary DeploymentsIt adds only a runtime choice to the current contract.
A Spin-native build/deploy experience and a controller that understands SpinAppSpin OperatorIt turns Spin application intent into Kubernetes resources.
A broad distributed component model, runtime hosts, capability providers, and lattice networkingEvaluate wasmCloud separatelyThat 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.

Sources

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex