
Table of Contents
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.
spin containerd shim on your nodes, and apply a SpinApp manifest pointing to your OCI-stored module. The operator schedules Wasm workloads as native pods without requiring Dockerfiles or heavy base images.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.
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.tmplwith 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.
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.
| Criteria | Standard Container | Knative / Lambda | SpinKube (Wasm) |
|---|---|---|---|
| Cold Start Latency | 500ms – 2s | 100ms – 500ms | < 10ms |
| Memory Overhead | 50MB+ (OS + Runtime) | 128MB min typical | 2MB – 10MB |
| Isolation Boundary | Kernel namespaces/cgroups | VM or Container | Wasm Sandbox (Capability-based) |
| Image Size | 100MB – 1GB+ | N/A (Managed) | 1MB – 5MB |
| Ecosystem Maturity | Very High | High | Growing (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.
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.