LabHub

Blog

WebAssembly Server-Side and WASI 2026 Deep Dive — Spin, Wasmtime, Wasmer, WasmEdge, wasmCloud, Extism, JCO, and the Component Model

한국어English日本語

Prologue — In 2026, WASM Is Not "Fast Assembly in the Browser"

Solomon Hykes (the Docker founder) tweeted a now-famous line back in 2019:

"If WASM+WASI existed in 2008, we wouldn't have needed to create Docker."

In 2026 that quote has stopped being aspirational and started being descriptive.

This article walks through that landscape in 24-28 chapters. Every code snippet works on real toolchains as of May 2026, and every referenced URL is real.

One-line takeaway: "Where does my code run, with what authority, and who composes it?" Those two questions decide 90% of WASM tooling choices in 2026.


Chapter 1 · Revisiting the Essence of WASM — Assembly, Isolation, Portability

WebAssembly is often introduced as "fast assembly," but its real essence rests on three axes.

AxisMeaningResult
Portable bytecodeStack-machine-based virtual ISASame binary runs anywhere
Sandboxed by defaultNo direct access to host memory or syscallsClear trust boundary
DeterministicDeterministic minus floating-point NaN quirksGreat cacheability and reproducibility

The browser was just stage one. Since the 2017 MVP, the core spec has expanded to include reference types, multi-value, bulk memory, SIMD, threads, tail call, GC, exception handling, multi-memory, and function references. As of 2026, GC and exception handling are stable across every major runtime, which lets GC-heavy languages such as Java, Kotlin, OCaml, and Dart compile naturally.

Crucial mental shift: in 2026, WASM is not "a fast VM for C/Rust." It is "a language-neutral component ABI." That is why the Component Model in the next chapter is the real story.


Chapter 2 · WASI Preview 1 vs Preview 2 — From POSIX Mimicry to Capability Interfaces

Early WASI (WebAssembly System Interface) was a partial POSIX imitation. Functions like fd_read, fd_write, and path_open lived in a flat wasi_snapshot_preview1 interface that Rust's wasm32-wasi target called directly. Effectively, "POSIX lent by the runtime."

WASI Preview 2 (WASI 0.2) is different.

This is capability-based security in practice. There is no ambient authority floating around. A module can only do what it imported. Compared to container seccomp/capabilities/AppArmor, the model is far more explicit and composable.

The P1 to P2 migration was mostly done in 2025. Rust's wasm32-wasip2 target, TinyGo's wasip2, and jco's P2 component generation are the standard paths.


Chapter 3 · The Component Model — A Language-Neutral ABI as a Major Event

The Component Model adds two layers on top of WASM.

  1. Core module — the existing .wasm: functions, memory, tables, globals.
  2. Component — bundles multiple core modules and exposes WIT types (record, variant, list, option, result, resource, tuple, string) as external interfaces.

What the Component Model solves is ABI negotiation across languages. C and Rust manage memory directly. Go has a GC. JavaScript has GC plus JIT. They cannot call each other's functions directly. The Component Model sits between them with a canonical ABI and uses lift/lower operations to convert types.

Here is a snippet of a WIT interface definition.

package example:greeter@0.2.0;

interface api {
    /// Build a greeting.
    greet: func(name: string) -> string;

    /// A counter resource.
    resource counter {
        constructor();
        increment: func();
        get: func() -> u64;
    }
}

world greeter {
    export api;
    import wasi:clocks/wall-clock@0.2.0;
    import wasi:logging/logging@0.2.0;
}

From this one WIT file:

The caller does not know which language implemented the component. That is the promise of the Component Model.


Chapter 4 · Wasmtime — The Bytecode Alliance Reference Runtime

Wasmtime is effectively the reference implementation of WASM, maintained by the Bytecode Alliance. It is written in Rust and uses Cranelift codegen to AOT/JIT compile to x86_64, aarch64, and s390x.

Wasmtime 26.x is the current stable line in 2026. Highlights:

Wasmtime's beauty is that host embedding is straightforward. Here is the Rust flow for loading a component and calling a function:

use wasmtime::{Engine, Config, Store};
use wasmtime::component::{Component, Linker};

fn main() -> anyhow::Result<()> {
    let mut config = Config::new();
    config.wasm_component_model(true);
    config.async_support(false);

    let engine = Engine::new(&config)?;
    let component = Component::from_file(&engine, "greeter.wasm")?;

    let mut linker = Linker::new(&engine);
    wasmtime_wasi::add_to_linker_sync(&mut linker)?;

    let wasi = wasmtime_wasi::WasiCtxBuilder::new()
        .inherit_stdio()
        .build_p1();
    let mut store = Store::new(&engine, wasi);

    let instance = linker.instantiate(&mut store, &component)?;
    let greet = instance.get_typed_func::<(String,), (String,)>(&mut store, "greet")?;
    let (out,) = greet.call(&mut store, ("WASI 2026".into(),))?;
    println!("{out}");
    Ok(())
}

Spin, wasmCloud, and the Lambda Adapter all variant this same pattern internally.


Chapter 5 · Wasmer — Multi-Backend WASM and a Package Manager Mindset

Wasmer takes a different tack. From day one it offered multiple compiler backends (Cranelift, LLVM, Singlepass), and it shipped WAPM (the WebAssembly Package Manager) as an npm-style distribution channel.

In 2026, Wasmer 5.x leans into the following strengths:

Running Python on Wasmer is a one-liner:

wasmer run python/python --mapdir=/app:./app -- /app/main.py

WAPM bundles packages on top of the Component Model. In 2026, interop with OCI registries (GHCR, Docker Hub) accelerated, and pushing WASM components to ghcr.io is now a standard flow.

Wasmer's P2 support lagged Wasmtime briefly but caught up in 5.x. The combination of WASIX and P2 in one runtime is Wasmer's biggest differentiator.


Chapter 6 · WasmEdge — A Runtime Tuned for Cloud-Native and AI

WasmEdge went from CNCF Sandbox through Incubating and reached near-Graduated status in 2025. It is purpose-built for CloudOS, CDN, and AI inference.

Since Docker Desktop 4.x, WasmEdge runs with a single flag: docker run --runtime=io.containerd.wasmedge.v1. On Kubernetes, you define a RuntimeClass:

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: wasmedge
handler: wasmedge
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-wasm
spec:
  replicas: 3
  selector:
    matchLabels: { app: hello-wasm }
  template:
    metadata:
      labels: { app: hello-wasm }
    spec:
      runtimeClassName: wasmedge
      containers:
        - name: hello
          image: ghcr.io/example/hello-wasm:0.1.0

WasmEdge has cleanly positioned itself as "the easiest way to run WASM on Kubernetes."


Chapter 7 · Spin (Fermyon) — A WASM Microservices Framework

Fermyon's Spin is something like "Express or Flask on top of WASM." One component plays an HTTP handler, Redis consumer, or cron trigger; components talk to one another through WIT.

Spin's core pieces:

A minimal HTTP component manifest:

spin_manifest_version = 2

[application]
name = "hello-spin"
version = "0.1.0"

[[trigger.http]]
route = "/hello/..."
component = "hello"

[component.hello]
source = "target/wasm32-wasip2/release/hello.wasm"
allowed_outbound_hosts = ["https://api.example.com"]
key_value_stores = ["default"]
sqlite_databases = ["default"]

[component.hello.build]
command = "cargo build --target wasm32-wasip2 --release"
watch = ["src/**/*.rs", "Cargo.toml"]

Spin runs locally as a single binary (spin up) and deploys to Fermyon Cloud or to Fermyon Platform for Kubernetes via spin-operator. Think of it as "a WASM-native operational system for microservices."

In 2026, Spin integrated with LangChain/LlamaIndex-style AI workflows (spin-llm) and expanded into the edge AI server category.


Chapter 8 · wasmCloud — WASM on a Distributed Actor Model

wasmCloud is a CNCF Incubating project that bundles "WASM components + actor model + NATS messaging." Its core ideas:

This is dependency inversion, full stop. A component does not "call Redis." It "calls wasi:keyvalue/store." Whether the actual store is Redis, NATS KV, or Postgres is bound at deploy time by an operator in wadm (the wasmCloud Application Deployment Manager).

apiVersion: core.oam.dev/v1beta1
kind: Application
metadata:
  name: hello-wasmcloud
spec:
  components:
    - name: hello
      type: component
      properties:
        image: ghcr.io/example/hello:0.1.0
      traits:
        - type: spreadscaler
          properties:
            instances: 5
        - type: link
          properties:
            target: redis-kv
            namespace: wasi
            package: keyvalue
            interfaces: ["store"]
    - name: redis-kv
      type: capability
      properties:
        image: ghcr.io/wasmcloud/redis-kv:0.10.0

The ability to span the same lattice across multiple clouds, edge sites, and on-prem is what wasmCloud sells. Adobe, BMW, and Bosch appear as conference case studies.


Chapter 9 · Lunatic — A WASM Runtime Inspired by Erlang/Elixir

Lunatic carries BEAM (the Erlang VM) ideas — actors, supervisor trees, preemptive scheduling — into WASM. It is built in Rust and uses Wasmtime as its core.

It shines in workloads where concurrency dominates:

Where Spin is "a bundle of HTTP handlers," Lunatic is "BEAM on top of WASM." The two are not competitors; they live in different niches.


Chapter 10 · Extism — A Standard for Embedding WASM Plugins in Host Apps

Extism focuses on one question: "I want to extend my app with WASM plugins." Dylibso maintains it as OSS.

Common scenarios:

Extism trades some of the Component Model's generality for integration simplicity. As of 2026 Extism 1.x is stable, and pulling plugins from OCI registries via extism call oci://... is standard.


Chapter 11 · JCO and StarlingMonkey — JavaScript Becomes a Component Too

JCO (JavaScript Component Object) is the JavaScript ↔ Component bridge from the Bytecode Alliance. jco 1.x is the stable line in 2026.

Key capabilities:

StarlingMonkey is a slimmed-down SpiderMonkey for embedding — the next-generation JS-on-WASM runtime after Cloudflare workerd. Fastly Compute and Fermyon Spin picked StarlingMonkey for their JS component runtime.

JCO's real superpower is TypeScript type generation. It produces .d.ts files from WIT so callers get type-safe access to every component function and resource.

// app.js — input to jco componentize
import { now } from 'wasi:clocks/wall-clock@0.2.0';
import { log } from 'wasi:logging/logging@0.2.0';

export const api = {
  greet(name) {
    const { seconds } = now();
    log('info', 'greeter', `now=${seconds}`);
    return `Hello, ${name}! It is now ${seconds}.`;
  },
};

This single file, after jco, becomes a component that can run on any host: Wasmtime, Spin, wasmCloud, or Cloudflare Workers.


Chapter 12 · Polyglot Compilation Paths — Rust, Go, C/C++, AssemblyScript, Python, Java, .NET

A short table of the canonical 2026 compilation paths.

LanguageToolingTargetNotes
Rustrustc + cargo-componentwasm32-wasip2First-class support, shortest path
C/C++wasi-sdk (clang 19+)wasm32-wasip2Componentize via wit-bindgen-c
GoTinyGo 0.34+wasip2Standard Go covers wasip1; TinyGo is faster for wasip2
AssemblyScriptasc 0.27+wasm32TS-like syntax, small binaries
JavaScriptjco + StarlingMonkeycomponentSee chapter 11
Pythoncomponentize-pycomponentBundles CPython into a WASM component
JavaTeaVM, CheerpJ, Spasmwasm32-gcJVM bytecode to WASM-GC
C# / .NETwasi-experimental + NativeAOTwasi.NET 9 is finalizing WASI P2 support
Rubyruby.wasmwasiOfficial WASM builds since Ruby 3.3
Zigzig build -target wasm32-wasiwasiComponentize with wasm-tools

The big 2024-2025 shift is that Python, Java, and .NET became componentizable. The old "C/Rust only" framing is obsolete.


Chapter 13 · WIT and wit-bindgen — Interface-First Development

How the Component Model really works in practice: "write the WIT first, pick the implementation language second."

wit-bindgen reads WIT and emits bindings for each language. Rust example:

wit_bindgen::generate!({
    world: "greeter",
    path: "./wit",
});

use exports::example::greeter::api::Guest;

struct Component;

impl Guest for Component {
    fn greet(name: String) -> String {
        format!("Hello, {name} from Rust WASM!")
    }
}

export!(Component);

Build and componentize with wasm-tools:

cargo build --target wasm32-wasip2 --release
wasm-tools component new \
  target/wasm32-wasip2/release/greeter.wasm \
  -o greeter.component.wasm
wasm-tools validate greeter.component.wasm

The resulting greeter.component.wasm is identical whether you run it on Wasmtime, Spin, jco, or wasmCloud. That is the promise of the Component Model.


Chapter 14 · Cloudflare Workers and WASM — Modules on Top of V8 Isolates

Cloudflare Workers uses V8 isolates as its primary isolation unit and supports WASM as a first-class module.

A typical Rust to WASM to Workers flow:

cargo install wasm-pack
wasm-pack build --target web
wrangler deploy

Workers is powerful, but it does not support every aspect of the Component Model (arbitrary host imports, for example). Cloudflare instead supplies its own built-in bindings (KV, R2, D1, Queues, AI, Durable Objects).


Chapter 15 · Fastly Compute@Edge — The Prototype WASM-First Edge

Fastly Compute@Edge has been built on WASM (Wasmtime under the hood) since day one. There is no V8 isolate in between — WASM instances run directly.

Fastly's strengths are logging and real-time metrics and a broad set of certifications (SOC2, PCI, HIPAA). Media, e-commerce, and financial services tend to live on Fastly Compute.


Chapter 16 · AWS Lambda Web Adapter and WASM — Running Components on Lambda

As of 2026 AWS Lambda has no first-class WASM runtime. Two workarounds are standard:

  1. AWS Lambda Web Adapter — run Wasmtime inside a Lambda container image and execute components there. See the awslabs/aws-lambda-web-adapter GitHub repository.
  2. Fermyon Spin on Lambda — package Spin components inside the Lambda Adapter.

Lambda's ~350ms cold start versus Wasmtime's microsecond cold start is a tax the user pays. So serious WASM edge workloads tend to land on Fastly, Cloudflare, Fermyon Cloud, Cosmonic, or Wasmer Edge instead.

That said, AWS announced in late 2025 that Bedrock will accept WASM-based custom functions. Lambda gaining first-class WASM support within the next year or two is plausible.


Chapter 17 · Docker WASM and containerd-wasm-shims — WASM Without Containers

Docker Desktop 4.15+ shipped WASM workloads as official beta, and the feature went GA in 2025. The implementations are containerd-shim-wasmtime, containerd-shim-wasmedge, containerd-shim-wasmer, and containerd-shim-spin.

If an OCI image contains a WASM binary, one flag — --runtime=io.containerd.wasmedge.v1 — runs it on the node. The container runtime starts a WASM instance instead of a container.

Kubernetes does the same via RuntimeClass. The result: a single node hosts both containers and WASM, and the scheduler matches workloads to runtimes automatically.


Chapter 18 · Capability-Based Security — Why WASM's Trust Boundary Is Tighter Than Containers'

Container isolation combines namespaces, cgroups, seccomp, and AppArmor in the Linux kernel. Powerful, but the trust depends on a single kernel — and one CVE can collapse the whole boundary.

WASM's isolation is different.

This model gives WASM a tighter trust boundary than containers. Containers say "you get everything; subtract what you don't need." WASM says "you get nothing; add what you do need."

For enterprise security teams the gap is enormous. Multi-tenant SaaS, user-code execution (Cloudflare Workers, Replit, Modal), untrusted plugins — in every one of these scenarios WASM is a safer answer than containers.


Chapter 19 · Performance — Cold Start, JIT, AOT, and Pre-Instantiation

Performance has no single answer. Separate three phases.

  1. Compilation — turning .wasm bytecode into native code. JIT (Wasmtime default) versus AOT (WasmEdge, Wasmer LLVM, Wasmtime --cache).
  2. Instantiation — attaching memory, tables, and globals to a compiled module to produce an executable instance.
  3. Execution — actually calling functions.

The cold-start secret sauce is pre-instantiation and pooling.

Execution speed with the LLVM backend lands at 70-95% of native. SIMD, threads, and tail-call enabled workloads (image processing, crypto, ML inference) often exceed 90%.

Cold start comparison (numbers reported as of May 2026):

RuntimeCold start
AWS Lambda (Node)~350ms
Cloudflare Workers (JS isolate)~5ms
Fastly Compute@Edge (WASM)<1ms
Wasmtime + pooling~50µs
WasmEdge AOT~30µs

Chapter 20 · Polyglot Demo — Component in Rust, Called from JS

This chapter shows the Component Model in action. The WIT is the same greeter from chapter 3.

Rust implementation (src/lib.rs):

wit_bindgen::generate!({ world: "greeter", path: "./wit" });

use exports::example::greeter::api::{Counter, Guest, GuestCounter};
use std::cell::Cell;

struct Component;

impl Guest for Component {
    fn greet(name: String) -> String {
        format!("Hi, {name}!")
    }
    type Counter = CounterImpl;
}

struct CounterImpl(Cell<u64>);

impl GuestCounter for CounterImpl {
    fn new() -> Self { Self(Cell::new(0)) }
    fn increment(&self) { self.0.set(self.0.get() + 1); }
    fn get(&self) -> u64 { self.0.get() }
}

export!(Component);

Build it and call from JS:

cargo build --target wasm32-wasip2 --release
wasm-tools component new target/wasm32-wasip2/release/greeter.wasm -o greeter.wasm
jco transpile greeter.wasm -o out --name greeter
node -e "import('./out/greeter.js').then(m => { const c = new m.api.Counter(); c.increment(); c.increment(); console.log(m.api.greet('JS'), c.get()); })"

The very same .wasm runs unchanged on Spin, wasmCloud, and the Wasmtime CLI. Polyglot is not a slide deck; it is executable code.


Chapter 21 · WASM and AI — LLM Inference and ggml on WASM

This momentum took off in 2024.

WasmEdge ggml's pitch is that models ship in an OS- and hardware-independent way. Drop the same .wasm onto Linux x86_64, macOS arm64, Raspberry Pi, or AWS Graviton and inference starts. That is a game-changer for embedded and edge AI.


Chapter 22 · LINE, NHN Cloud, Cybozu — WASM Adoption in Korea and Japan

In Korea and Japan, infrastructure and content companies are leading adoption.

The common thread: WASM lands where multi-tenant, plugin, or edge isolation problems cannot be solved by containers alone.


Chapter 23 · Operations — Observability, Debugging, Deployment

As WASM has matured into production, observability has caught up.

Debugging is still evolving, but in 2025 the combination of .wasm with preserved DWARF debug info, Wasmtime's --gdb mode, and Chrome DevTools' WASM debugger reached real-world usability. AssemblyScript and Rust emit good DWARF; Go still has gaps.

For deployment, pushing component .wasm files to OCI registries (ghcr.io, docker.io) and pulling them from Spin, wasmCloud, or Knative in a GitOps loop is the standard.


Chapter 24 · The Future — wasi-preview3, async/streams, wasi-gpu, wasi-threads

What's already on the horizon in mid-2026:

WASM is no longer a single spec but an IETF/W3C-style multilayer ecosystem governing interfaces.


Chapter 25 · Decision Tree — Which Runtime for Which Workload?

A practical guide to close.

WorkloadRecommendation
HTTP APIs at the edgeFastly Compute@Edge, Cloudflare Workers, Spin + Fermyon Cloud
Sidecars in your own K8s clusterWasmEdge + RuntimeClass, Spin + spin-operator
Distributed multi-cloud microserviceswasmCloud + NATS
AI inference (edge/embedded)WasmEdge ggml, Wasmer + Burn
Plugins inside a host appExtism
Concurrency-heavy servicesLunatic
Running user code (multi-tenant)Wasmtime + epoch interruption + pooling
Layering on top of LambdaLambda Web Adapter + Wasmtime

Four decision axes:

  1. Trust boundary — does user-supplied code show up? If yes, WASM.
  2. Cold start — do you need sub-millisecond? Then Fastly, Spin, or WasmEdge AOT.
  3. Language diversity — polyglot? Pick Component Model first-class hosts (Wasmtime/Spin/wasmCloud).
  4. Operational model — do you want K8s standards, or hand it off to a PaaS?

Answer those four questions and the tool almost picks itself.


Chapter 26 · Wrap-Up — Not "Post-Docker" but "Stronger Isolation on Top of Docker"

People ask whether WASM "will replace containers." The 2026 field answer is unambiguous: WASM complements containers; it does not replace them.

The two coexist on K8s nodes. RuntimeClass routes workloads to the right runtime. Solomon Hykes's tweet was less "WASM would have replaced Docker" and more "WASM can solve some of the same problems Docker addressed, with a different approach."

These 26 chapters traced that different approach end to end. From writing one line of WIT — to that single line being implemented in Rust, Go, JS, or Python — to running unchanged on Wasmtime, Spin, wasmCloud, Fastly, and Cloudflare — the 2026 picture is a world where all of that happens at once.

"Where does my code run, with what authority, and who composes it?" Answer those two questions clearly and you can carry the answer around as a single .wasm.


References

Comments

No comments yet.

Sign in to leave a comment