Skip to content

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.

Terminal window
# wasmtime — via installer script
curl https://wasmtime.dev/install.sh -sSf | bash
# wasmer — via installer script
curl https://get.wasmer.io -sSfL | sh
# Verify
wasmtime --version
wasmer --version
Terminal window
# Basic execution
wasmtime my_app.wasm
# Pass command-line arguments (-- separates runtime flags from module args)
wasmtime my_app.wasm -- hello world
# Grant access to a directory
wasmtime --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 variables
wasmtime --env DATABASE_URL=postgres://localhost/db my_app.wasm
# Combine: dir + env + args
wasmtime --dir /data --env LOG_LEVEL=info my_app.wasm -- --config /data/cfg.toml
Terminal window
# Basic execution
wasmer my_app.wasm
# Mount a directory
wasmer --dir /tmp my_app.wasm
# Pass environment variables
wasmer --env KEY=value my_app.wasm
# Run from the Wasmer registry (wapm packages)
wasmer run python/python -- --version

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
Embedding wasmtime to run a plugin module
Which flag grants a WASI module access to a host directory in wasmtime?
What is the purpose of WasiCtxBuilder when embedding wasmtime in Rust?
What does ahead-of-time (AOT) compilation achieve in wasmtime?