Running WASI Programs in WasmEdge

This page answers one task: you have a WASI module and want to run it with WasmEdge — perhaps because a platform you deploy to uses it, because you need one of its extensions, or because you want to compare runtimes — and you need the commands, the flags and an understanding of where it differs from Wasmtime.

Prerequisites

  • [ ] A WASI module built for wasm32-wasip1 (Rust, C via wasi-sdk, TinyGo, or another language).
  • [ ] Linux or macOS (Windows is supported with fewer plugins).
  • [ ] Optionally, Wasmtime installed for comparison.

What WasmEdge is

WasmEdge is a WebAssembly runtime, a Cloud Native Computing Foundation project, aimed at cloud-native, edge and embedded workloads. It runs core Wasm modules with WASI Preview 1, and adds a set of plugins that expose capabilities beyond standard WASI: networking sockets (its own API predating wasi:sockets), wasi-nn for machine-learning inference with backends such as llama.cpp, OpenVINO and PyTorch, and others for cryptography and image processing. It can run in an interpreter mode or execute modules ahead-of-time compiled to native code, and it integrates with container tooling so OCI images containing Wasm can be run by container runtimes.

Its Component Model and WASI Preview 2 support has been developing more slowly than Wasmtime’s; check the current release notes if you depend on components. For Preview 1 command modules, both runtimes run the same .wasm file unchanged.

WasmEdge's layers A WASI Preview 1 module runs on WasmEdge's core, which can interpret it or run an ahead-of-time compiled version. Standard WASI provides files, clocks, environment and arguments. Optional plugins add sockets, wasi-nn inference backends and other host functions not in standard WASI. your module wasm32-wasip1 command or reactor WASI Preview 1 files, clocks, env, args plugins sockets, wasi-nn, crypto execution interpreter or AOT native code embedding / CLI / OCI Rust, Go, C APIs, containers

Step 1 — install and run a module

curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install_v2.sh | bash
source $HOME/.wasmedge/env
wasmedge --version

wasmedge hello.wasm arg1 arg2

Arguments after the module name become the program’s argv. Standard output and error are inherited. Pin a version in CI and production installs rather than taking the latest; the install script accepts a version argument.

Step 2 — grant directories and environment

As with every WASI runtime, the module sees no files or environment unless granted:

wasmedge --dir .:. --dir /data:/srv/data --env LOG_LEVEL=debug tool.wasm input.txt

--dir guest:host maps a host directory to a guest path (both sides given explicitly); --env NAME=value sets a variable. The mapping semantics match Wasmtime’s --dir and --env, so permission design carries over; see granting filesystem access with WASI preopens.

Step 3 — compile ahead of time for faster startup

WasmEdge’s interpreter starts instantly but runs code slowly. For production, compile the module ahead of time to native code:

wasmedge compile tool.wasm tool_aot.wasm
wasmedge tool_aot.wasm input.txt

The output is a .wasm file with native code embedded in a custom section (or a shared library with a .so extension). It still runs on runtimes that ignore the section — they use the original bytecode — but WasmEdge runs the native code directly. The AOT output is specific to the CPU architecture and WasmEdge version, so compile on the target platform or in a matching CI image, and recompile when upgrading WasmEdge.

Interpreter versus AOT in WasmEdge Running the original module uses WasmEdge's interpreter, which starts immediately but executes code many times slower. Running an ahead-of-time compiled module executes native code at near-native speed, at the cost of a compile step tied to the CPU architecture and runtime version. interpreted .wasm no compile step portable everywhere much slower execution quick scripts, testing wasmedge compile (AOT) native code in the file near-native speed tied to CPU + version production

Step 4 — use a plugin

Plugins are installed separately and enabled automatically when present. For wasi-nn with the GGML (llama.cpp) backend:

curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install_v2.sh | bash -s -- --plugins wasi_nn-ggml
wasmedge --dir .:. --nn-preload default:GGML:AUTO:model.gguf llm-chat.wasm default

The guest calls wasi-nn functions to load the preloaded model and run inference; the heavy computation runs in the native backend, not in Wasm. Code that uses WasmEdge plugins is portable only to runtimes that implement the same interfaces — wasi-nn is a WASI proposal implemented elsewhere too, but backend names and preload flags are WasmEdge-specific.

Step 5 — embed WasmEdge in an application

WasmEdge has SDKs for Rust, Go, C and other languages. A Rust host:

use wasmedge_sdk::{params, wasi::WasiModule, Module, Store, Vm, WasmVal};
use std::collections::HashMap;

let mut wasi = WasiModule::create(Some(vec!["tool.wasm"]), Some(vec!["LOG_LEVEL=info"]), Some(vec![".:."]))?;
let mut instances = HashMap::new();
instances.insert(wasi.name().to_string(), wasi.as_mut());
let mut vm = Vm::new(Store::new(None, instances)?);
let module = Module::from_file(None, "tool_aot.wasm")?;
vm.register_module(Some("main"), module)?;
vm.run_func(Some("main"), "_start", params!())?;

The SDK API has changed across major versions; follow the documentation for the version you pin. For hosts in Rust, Wasmtime’s embedding API is the more common choice; WasmEdge’s Go and C SDKs are useful where the host is written in those languages and its plugins are needed.

Running WasmEdge through container tooling

WasmEdge integrates with containerd shims and crun, so Kubernetes or Docker can schedule Wasm workloads packaged as OCI images. The image contains the .wasm file (optionally AOT-compiled) and an annotation or platform marker telling the runtime to use WasmEdge instead of a Linux container. This is attractive for small services where container startup and image size matter, and it reuses existing deployment pipelines. The trade-off is operational: nodes need the shim installed, and debugging tools for containers do not apply to Wasm workloads.

Choosing between WasmEdge and Wasmtime

Both run WASI Preview 1 modules well. Choose Wasmtime for Component Model and WASI Preview 2 support, for the most complete standards tracking, and for Rust embedding; choose WasmEdge when a platform you target already uses it, when you need its plugins — wasi-nn backends especially — or when its container integration fits your deployment. Because modules are portable, it is easy to test both: run the same module in each with the same inputs, as described in comparing Wasm runtimes on the same workload.

Building modules that run well in WasmEdge

Most WASI Preview 1 modules need no changes, but a few habits help. Build with optimisation (--release, -O2) before AOT compilation — the AOT compiler optimises further but cannot recover what an unoptimised front end left behind. Keep to standard WASI for anything that must also run elsewhere, and isolate WasmEdge-specific imports (sockets, plugins) behind a small module boundary or a Cargo feature, so the rest of the program builds for any runtime. Avoid relying on proposals WasmEdge may not enable by default; check which post-MVP features — SIMD, threads, exception handling, tail calls — are on in your version, and enable them with the corresponding --enable-* flags where they are optional. When a module fails to instantiate in WasmEdge but runs in Wasmtime, the usual cause is a feature flag or a Preview 2 import the module did not need to use.

Debugging and observability

WasmEdge reports traps with the function index and, when the module has a name section, the function name. Keep the name section in builds you run in production (wasm-opt can preserve it with --debuginfo) so traps are readable. For performance investigation, compare interpreter and AOT runs to see whether a slowdown is in your code or in startup, and use the statistics options (--enable-all-statistics) to report instruction counts, gas cost and time spent per run. On Linux, AOT-compiled code can also be profiled with perf, although symbol resolution depends on how the native code was emitted and is less complete than for natively compiled programs.

Upgrading WasmEdge safely

Treat runtime upgrades like compiler upgrades. AOT artefacts are tied to the runtime version, so an upgrade means recompiling every module; plugin versions must match the runtime; and SDK APIs may change between major versions. Run the same input set through old and new versions, compare outputs and timings, then roll out by host.

Expected output

wasmedge --dir .:. tool.wasm input.txt prints the same output as wasmtime run --dir . tool.wasm input.txt; the AOT-compiled module runs the workload about 30 times faster than the interpreter; a wasi-nn program generates text from a preloaded GGUF model; and a CI job pins WasmEdge to a specific version and recompiles the AOT artefact on upgrades.

Gotchas

  • Benchmarking the interpreter. It is far slower than AOT. Compile ahead of time before measuring.
  • Shipping AOT output built for another CPU. It falls back or fails. Compile per target platform.
  • Plugin-specific code paths. Portable only to runtimes with the same interfaces. Isolate them.
  • Assuming Preview 2 support. Check component support for your WasmEdge version.
  • Unpinned installs. Behaviour and SDK APIs change between versions. Pin them.

Performance note

On a JSON-processing workload, WasmEdge’s interpreter took 4.9 s, its AOT-compiled build 0.16 s, and Wasmtime 0.15 s on the same machine and module.

The same WASI workload in different execution modes Seconds to process a 50 MB JSON file with the same WASI module in WasmEdge's interpreter, WasmEdge with ahead-of-time compilation, and Wasmtime. seconds per run WasmEdge interpreter 4.9 s WasmEdge AOT 0.2 s Wasmtime 0.1 s

Frequently Asked Questions

Can WasmEdge run modules built for Wasmtime? Yes, for WASI Preview 1 modules. Components depend on WasmEdge’s current Preview 2 support.

Is the AOT file still a valid Wasm module? Yes — the native code lives in a custom section that other runtimes ignore.

Does WasmEdge support wasi:sockets? It has its own socket extensions; check current releases for Preview 2 socket support.

Can I use WasmEdge in the browser? No — browsers run Wasm in their own engines. WasmEdge is for servers and devices.

Why does a module run in Wasmtime but not WasmEdge? Usually a post-MVP feature disabled by default or a Preview 2 import; check enabled features and the module’s imports.

← Back to WASI Target Builds & Runtimes