wasmtime & wasmer
Two production-grade standalone runtimes dominate the WASI ecosystem: wasmtime (maintained by the Bytecode Alliance, written in Rust) and wasmer (written in Rust, cross-platform). Both implement the WASI spec, support ahead-of-time (AOT) compilation, and expose embedding APIs for Rust, Python, Go, and other languages.
Installing the runtimes
Section titled “Installing the runtimes”# wasmtime — via installer scriptcurl https://wasmtime.dev/install.sh -sSf | bash
# wasmer — via installer scriptcurl https://get.wasmer.io -sSfL | sh
# Verifywasmtime --versionwasmer --versionRunning a WASI module with wasmtime
Section titled “Running a WASI module with wasmtime”# Basic executionwasmtime my_app.wasm
# Pass command-line arguments (-- separates runtime flags from module args)wasmtime my_app.wasm -- hello world
# Grant access to a directorywasmtime --dir /tmp my_app.wasm
# Grant access with a path alias (host path::guest path)wasmtime --dir /host/data::/data my_app.wasm
# Pass environment variableswasmtime --env DATABASE_URL=postgres://localhost/db my_app.wasm
# Combine: dir + env + argswasmtime --dir /data --env LOG_LEVEL=info my_app.wasm -- --config /data/cfg.tomlRunning a WASI module with wasmer
Section titled “Running a WASI module with wasmer”# Basic executionwasmer my_app.wasm
# Mount a directorywasmer --dir /tmp my_app.wasm
# Pass environment variableswasmer --env KEY=value my_app.wasm
# Run from the Wasmer registry (wapm packages)wasmer run python/python -- --versionEmbedding wasmtime in a Rust application
Section titled “Embedding wasmtime in a Rust application”The real power of standalone runtimes is embedding — loading .wasm plugins at runtime without recompiling the host application.
use wasmtime::{Engine, Linker, Module, Store};use wasmtime_wasi::WasiCtxBuilder;
fn run_plugin(wasm_bytes: &[u8]) -> anyhow::Result<()> { let engine = Engine::default(); let module = Module::new(&engine, wasm_bytes)?;
// Build a minimal WASI context — no filesystem, no env vars let wasi = WasiCtxBuilder::new().inherit_stdio().build(); let mut store = Store::new(&engine, wasi);
let mut linker = Linker::new(&engine); wasmtime_wasi::add_to_linker_sync(&mut linker, |s| s)?;
let instance = linker.instantiate(&mut store, &module)?; let start = instance.get_typed_func::<(), ()>(&mut store, "_start")?; start.call(&mut store, ())?; Ok(())}This pattern is how plugin systems work: the host application loads arbitrary .wasm files at runtime and calls exported functions while the sandbox enforces strict isolation.
sequenceDiagram
participant Host as Host App (Rust)
participant WT as wasmtime Engine
participant M as Plugin (.wasm)
Host->>WT: Engine::default()
Host->>WT: Module::new(wasm_bytes)
Host->>WT: Linker + WasiCtxBuilder
Host->>WT: linker.instantiate()
WT->>M: instantiate + link imports
Host->>M: typed_func("process").call(args)
M-->>Host: return value