Scaling WASM Serverless Edge Functions (2449)
Introduction: Why WASM at the Edge?
WebAssembly (WASM) has emerged as the portable, sandboxed, near-native runtime ideal for edge serverless workloads. Unlike traditional containers, WASM modules start in microseconds, have a minimal memory footprint, and provide strong capability-based security. However, scaling thousands of distinct WASM functions across geographically distributed edge clusters introduces unique challenges: cold-start latency, resource quota enforcement, multi-tenancy isolation, and cross-cluster traffic routing.
This article presents the architecture we built at Votion Cloud to run 2,449 concurrent WASM functions per edge node with sub‑millisecond scheduling latency, leveraging Kubernetes primitives (CRDs, CRI‑O, Kube‑Scheduler extensions) and a custom WASM‑aware resource manager.
Architecture Overview
Control Plane
- Function CRD:
wasm.votion.io/v1alpha1.Functiondefines module OCI reference, resource limits, and scaling policies. - EdgeCluster CRD: Represents a physical edge site; holds capacity, network topology, and affinity rules.
- WASM Scheduler Plugin: Extends
kube-schedulerwith aScorefunction that prefers nodes with pre‑warmed runtimes (viawasmtimeorwasmedge) and available CPU/memory slices.
Data Plane
- Containerd shim (crun/wasm): Runs each function as a lightweight OCI container with
runwasi. - Sidecar Proxy (Envoy WASM): Handles mTLS, request routing, and per‑function rate limiting.
- Local Cache Layer:
wasm-cache-daemonpre‑fetches and verifies modules, reducing cold‑start to < 1.2 ms (p99).
The diagram below (rendered by the network-topology block) shows the request flow from client → edge ingress → function pod → backend services.
Cold‑Start Optimization Techniques
We measured cold‑start latency across three runtimes: Wasmtime, Wasmer, and Wasmedge. The table summarizes p50/p99 results for a 2 MB module on an Intel Xeon‑D (2.2 GHz, 8 vCPU).
Runtime | p50 (ms) | p99 (ms) | Memory (MiB)
----------|----------|----------|-------------
Wasmtime | 0.84 | 1.21 | 4.2
Wasmer | 1.12 | 1.68 | 5.1
Wasmedge | 0.71 | 1.05 | 3.8Key optimizations:
- AOT Compilation: Pre‑compile WASM to native code during CI/CD; store
.soartifacts in the OCI image. - Module Tiering: Frequently invoked functions keep a
warm poolof pre‑instantiated instances (configurable viaspec.scaling.warmPoolSize). - Shared Memory Snapshots: Use Linux
userfaultfdto fork a snapshot after initialization, then copy‑on‑write for new invocations.
eBPF/XDP kernel filter evaluates TCP/UDP frames directly on server NIC.
Multi‑Cluster Scheduling & Traffic Shaping
Edge clusters are federated via Karmada with a custom WASMSpreadPolicy that balances replicas based on:
- Real‑time CPU/memory pressure (Prometheus metrics).
- Network RTT to upstream dependencies (measured by sidecar).
- Data locality (e.g., GDPR‑restricted functions stay in EU clusters).
The scheduler plugin implements a PriorityMap that scores each candidate node:
func scoreNode(node *v1.Node, fn *wasmv1.Function) int64 {
base := 100
// Prefer nodes with warm runtime
if node.Annotations["wasm.votion.io/runtime-warmed"] == "true" {
base += 30
}
// Penalize high utilization
util := getCPUUtil(node)
base -= int64(util * 0.5)
// Affinity bonus
if matchesAffinity(node, fn.Spec.Affinity) {
base += 20
}
return base
}Traffic shaping uses Envoy’s wasm filter to enforce per‑function quotas and circuit‑breakers, preventing noisy‑neighbor effects.
Observability Stack
- Metrics: Prometheus +
wasm-exporter(exposes per‑function invocation count, latency, memory growth). - Tracing: OpenTelemetry WASM SDK injects trace context; Jaeger backend with edge‑aware sampling.
- Logging: Structured JSON logs shipped via Fluent Bit to Loki; labels include
function_id,edge_cluster,runtime.
Dashboard (Grafana) shows real‑time scaling events, cold‑start heatmaps, and cost per million invocations.
Benchmark Results: 2,449 Functions on a Single Node
We deployed a synthetic workload of 2,449 distinct WASM functions (average module size 1.8 MB) on a single edge node (Intel Xeon‑D 2.2 GHz, 32 vCPU, 128 GiB RAM). Each function received 10 RPS burst for 5 minutes.
- Scheduler latency: p99 3.4 ms (from Function CRD creation to pod Ready).
- Overall CPU utilization: 68 % (within target).
- Memory footprint: 4.2 GiB total (including warm pools).
- Error rate: 0.001 % (mostly transient network blips).
The chart-telemetry block above visualizes the scaling curve and resource saturation.
Conclusion & Next Steps
Running thousands of WASM serverless functions at the edge is achievable with a Kubernetes‑native control plane, a WASM‑optimized container runtime, and intelligent scheduling. Our implementation delivers sub‑millisecond cold starts, strong multi‑tenancy, and predictable cost.
Future work includes:
- WASM Component Model integration for fine‑grained capability delegation.
- GPU‑accelerated WASM (via
wasmtime-wasi-nn) for ML inference at the edge. - Cross‑edge state synchronization using CRDTs embedded in WASM modules.
Try the cli-builder to deploy a sample function to our Frankfurt edge cluster, and use the cost-estimator to project your workload spend.