Run Wasm on Kubernetes with SpinKube

Khimananda Oli 8 min read DevOps
Run Wasm on Kubernetes with SpinKube

By Khimananda Oli | Last reviewed: August 2026

You want to run Wasm on Kubernetes with SpinKube because traditional containers often feel too heavy for simple API glue code or edge logic. While standard Docker images carry gigabytes of OS overhead, WebAssembly modules start in milliseconds and offer superior isolation by default. This guide walks you through installing the SpinKube operator, configuring the containerd shim, and deploying your first SpinApp custom resource in a production-grade cluster.

Before diving into YAML, understand that this architecture fundamentally changes how the kubelet interacts with your workload. Unlike standard deployments where the container runtime unpacks a filesystem layer, here it delegates execution to a specialized shim. If you are managing stateful backend services alongside these lightweight functions, ensure your underlying data layer is solid; reviewing PostgreSQL administration essentials helps prevent bottlenecks when high-throughput Wasm functions hit your database.

SpinKube Runtime ArchitectureUser / CIspin build && pushOCI RegistryWasm Module ArtifactK8s API ServerSpinApp CRDNode (containerd + spin-shim)kubeletcontainerdspin-shim-v2Reconcile
High-level architecture to run Wasm on Kubernetes with SpinKube showing artifact flow from CI to the node-level shim.

How do you install the Spin Operator and runtime shim?

The foundation of any deployment where you run Wasm on Kubernetes with SpinKube consists of two distinct components: the cluster-level operator and the node-level runtime shim. Missing either one results in pending pods or FailedCreatePodSandBox errors. In my experience helping teams adopt this stack across AWS EKS and bare-metal k3s clusters, getting the shim configuration right is where most friction occurs.

Install the Spin Operator via Helm

The operator watches for SpinApp resources and translates them into standard Kubernetes Deployments and Services. Add the Fermyon Helm repository and install the operator into a dedicated namespace:

helm repo add spin-operator https://fermyon.github.io/spin-operator
helm repo update

helm install spin-operator spin-operator/spin-operator \
  --namespace spin-operator \
  --create-namespace \
  --version 0.3.0 \
  --wait

This deploys the controller manager, RBAC rules, and the CRDs. Verify the pod is running before proceeding:

kubectl get pods -n spin-operator
# NAME                              READY   STATUS    RESTARTS   AGE
# spin-operator-controller-manager  1/1     Running   0          45s

Configure the containerd shim on nodes

Kubernetes needs to know that when it sees a workload with the runtimeClassName: wasmtime-spin-v2, it should hand off execution to the Spin shim rather than runc. This requires modifying the containerd configuration on every node.

  • For k3s: Create /var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl with the shim binary path and restart the agent.
  • For kubeadm/EKS: Use a DaemonSet or machine bootstrap script to append the plugin configuration to /etc/containerd/config.toml.
# Append to containerd config
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.spin]
  runtime_type = "io.containerd.spin.v2"

After updating the configuration, restart containerd on each node. You can verify the runtime is registered by running crictl info and checking for the spin runtime entry. Without this step, the scheduler will never successfully place a Wasm pod.

How do you build and deploy a SpinApp to Kubernetes?

Once the infrastructure is ready, the workflow to run Wasm on Kubernetes with SpinKube shifts to application development. Unlike container workflows where you write a Dockerfile, here you compile directly to a Wasm module and push it to an OCI registry like GHCR, ECR, or Harbor.

Create and push the Wasm module

Initialize a new Spin app using the HTTP-Rust template (or Go, TypeScript, Python):

spin new http-rust hello-spinkube --accept-defaults
cd hello-spinkube

# Build and push to your registry
spin build
spin registry push ghcr.io/your-org/hello-spinkube:v0.1.0

The spin registry push command wraps the Wasm binary in an OCI artifact with the correct media types. This is critical; pushing a raw .wasm file as a Docker image layer will fail at runtime.

Define the SpinApp Custom Resource

Create a manifest named hello-spinkube.yaml. Note the runtimeClassName field, which must match the shim configuration from the previous section:

apiVersion: core.spinoperator.dev/v1alpha1
kind: SpinApp
metadata:
  name: hello-spinkube
spec:
  image: ghcr.io/your-org/hello-spinkube:v0.1.0
  replicas: 3
  runtimeClassName: wasmtime-spin-v2
  resources:
    limits:
      cpu: 500m
      memory: 128Mi
    requests:
      cpu: 100m
      memory: 64Mi

Apply the manifest and watch the rollout:

kubectl apply -f hello-spinkube.yaml
kubectl get spinapps
kubectl logs -l app=hello-spinkube --tail=50

The operator automatically creates a Service named hello-spinkube on port 80. You can port-forward to test locally immediately. For production traffic management, pair this with an ingress controller as described in Kubernetes ingress controllers explained.

SpinApp Deployment SequenceDeveloperOCI RegistrySpin OperatorKubelet/Shim1. Push Wasm Artifact2. Apply SpinApp YAML3. Pull Module4. Create Pod (spin-shim)5. HTTP Response Ready
Deployment sequence for SpinApp showing interaction between developer, registry, operator, and node shim.

Why choose SpinKube over standard containers or Knative?

When deciding whether to run Wasm on Kubernetes with SpinKube versus sticking with traditional containers or other serverless frameworks, the trade-offs center on density, security, and startup latency. I have benchmarked these workloads in SOC 2 compliant environments where auditability and resource efficiency were paramount.

CriteriaStandard ContainerKnative / LambdaSpinKube (Wasm)
Cold Start Latency500ms – 2s100ms – 500ms< 10ms
Memory Overhead50MB+ (OS + Runtime)128MB min typical2MB – 10MB
Isolation BoundaryKernel namespaces/cgroupsVM or ContainerWasm Sandbox (Capability-based)
Image Size100MB – 1GB+N/A (Managed)1MB – 5MB
Ecosystem MaturityVery HighHighGrowing (Early Production)

Standard containers remain superior for complex legacy applications requiring full OS access or specific kernel features. Knative excels when you need managed scale-to-zero without operating the runtime plane yourself. However, if your goal is maximum density on self-managed infrastructure with near-instant scaling, SpinKube offers a unique advantage. The capability-based security model also simplifies compliance reviews since Wasm modules cannot access the network or filesystem unless explicitly granted permissions in the manifest.

How do you handle observability and secrets in SpinKube?

A common mistake when teams first run Wasm on Kubernetes with SpinKube is assuming standard logging and secret injection work identically to Docker containers. They do not. Wasm modules are sandboxed; they cannot read environment variables directly unless the runtime passes them in, and stdout behaves differently depending on the shim version.

Structured logging and tracing

Spin apps should emit structured JSON logs to stdout. The spin-shim captures this and forwards it to the containerd log driver, making it compatible with Fluent Bit or Vector collectors. For distributed tracing, use the OpenTelemetry SDK built into the Spin framework. Configure the OTLP exporter endpoint via the SpinApp spec variables:

spec:
  variables:
    - name: OTEL_EXPORTER_OTLP_ENDPOINT
      value: "http://otel-collector.monitoring:4317"

This ensures traces flow to your backend without code changes. Refer to instrumenting an app with OpenTelemetry for language-specific SDK setup within Spin components.

Secrets management

Never bake secrets into Wasm modules. Use the Spin variables system integrated with Kubernetes Secrets. Define a variable in your spin.toml as secret, then map it in the SpinApp manifest:

variables:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: app-db-creds
        key: url

The operator injects this securely at runtime. The Wasm module accesses it via the Spin SDK's variables API, not std::env. This pattern maintains separation of concerns and satisfies audit requirements for secret handling in regulated environments.

Resource Density ComparisonStandard Node (Containers)Pod A (128Mi)Pod B (256Mi)Pod C (512Mi)Overhead~15-20 Pods / NodeSpinKube Node (Wasm)100+ Instances / Node5-10x Density
Visual comparison of pod density demonstrating why teams run Wasm on Kubernetes with SpinKube for high-concurrency workloads.

What are the production readiness considerations for SpinKube?

Running Wasm on Kubernetes with SpinKube in production requires addressing limitations that differ from standard container orchestration. First, understand that Wasm is single-threaded per instance by default in many runtimes; horizontal scaling via replicas is mandatory for CPU-bound tasks. Second, persistent storage is not directly supported inside the sandbox. You must use external object storage or databases via HTTP/gRPC triggers.

Network policies still apply at the pod level, but internal Wasm capabilities provide an additional defense layer. Explicitly declare allowed outbound hosts in your spin.toml:

[component.http-trigger]
allowed_outbound_hosts = ["https://api.example.com", "postgres://db.internal:5432"]

If a module attempts to reach an undeclared host, the request fails at the runtime level regardless of network policy. This zero-trust approach aligns perfectly with security-first architectures. Finally, monitor shim versions closely; the wasmtime-spin-v2 ecosystem evolves rapidly, and upgrading may require node drains to update the containerd plugin binary safely.

Start running Wasm on Kubernetes with SpinKube today

Migrating suitable microservices to Wasm reduces infrastructure costs and improves security posture significantly. Start with stateless API endpoints or event processors before tackling complex stateful systems. Test thoroughly in a staging environment that mirrors your production node configuration, especially the containerd shim setup. If you need help designing a secure, compliant Wasm architecture or integrating it with your existing observability stack, contact me to discuss your specific requirements.

Frequently Asked Questions

SpinKube integrates the Spin runtime into Kubernetes via KWasm, enabling native execution of WebAssembly components. It eliminates container overhead, reduces cold starts to milliseconds, and allows deploying lightweight, secure Wasm workloads using standard kubectl workflows alongside existing containerized applications in 2026 clusters.

Install the KWasm operator via Helm, then apply the SpinApp CRD manifest. Ensure your nodes support wasmCloud or have the spin shim installed. Verify installation by checking the kwasm-system namespace pods are running and the spin CLI can communicate with your cluster API server.

No.

SpinKube currently supports Spin SDK v3.x and v4.x stable releases. Components must target the WASI preview 2 interface. Older preview 1 modules require migration using wit-bindgen before deployment, as the runtime enforces strict component model compliance for security and interoperability guarantees.

Sub-millisecond typically.

Not directly within a single pod, but you can deploy SpinApp resources alongside standard Deployments in the same namespace. Use service mesh or ingress controllers to route traffic between Wasm components and containerized services, enabling hybrid architectures without modifying existing application deployments or networking configurations.

Start with 64Mi memory and 100m CPU per instance. Wasm components rarely exceed these bounds due to sandboxing. Monitor actual usage via Prometheus metrics exported by the Spin runtime, then adjust requests based on p99 latency data rather than guessing from container equivalents.

Check kubectl describe spinapp for scheduling errors, then inspect runtime logs via kubectl logs. Enable WASI trace logging by setting SPIN_LOG=trace in the spec. Validate your component locally with spin up before redeploying to isolate whether failures stem from code logic or cluster configuration issues.

Yes, including EKS, GKE, and AKS as of 2026. Managed providers now include KWasm node provisioning in their add-on marketplaces. Verify that your node pool uses a supported OS image with the containerd wasm shim preinstalled, or enable the managed Wasm runtime feature during cluster creation.

Wasm provides capability-based sandboxing by default, restricting filesystem, network, and environment access unless explicitly granted. Unlike containers sharing kernel syscalls, SpinKube executes components in isolated user-space runtimes, reducing attack surface even if the host kernel has vulnerabilities or misconfigured seccomp profiles.

Yes, through explicit WASI capability grants in the SpinApp manifest. Declare required permissions like outbound-http or postgres in the allowed_outbound_hosts field. The runtime enforces these at execution time, preventing unauthorized access while maintaining zero-trust principles without sidecar proxies or network policies.

Use WASI key-value interfaces backed by Redis, Valkey, or cloud-native stores. Local filesystem access is ephemeral and sandboxed. For persistent data, configure external storage capabilities in your SpinApp spec and bind credentials via Kubernetes secrets, avoiding volume mounts that break Wasm portability guarantees.

Modify the SpinApp manifest and apply changes with kubectl apply. The controller performs rolling updates by default, spinning up new Wasm instances before terminating old ones. Since cold starts are negligible, updates complete faster than container rollouts while maintaining availability through built-in health checks.

Yes, via KEDA or HPA targeting custom metrics exposed by the Spin runtime. Configure scaling triggers on request rate or concurrent executions rather than CPU/memory, since Wasm resource usage doesn't correlate linearly with load. Set minimum replicas to one to preserve instant responsiveness.

The Spin runtime catches traps and returns HTTP 500 responses without crashing the host process. Failed instances are automatically restarted by Kubernetes liveness probes. Stack traces appear in pod logs with source-mapped line numbers if debug symbols were included during build, enabling rapid diagnosis without node-level access.