How to Use Wazero: Run WebAssembly From Go (Embed, Host Functions, and the WASI CLI)

CodexFuse Team · 2026-11-06T15:11:57.843Z · 32 min read min read

How to Use Wazero: Run WebAssembly From Go (Embed, Host Functions, and the WASI CLI)

Learning how to use wazero takes about an hour the first time, from the install to a working Go program that calls a WebAssembly function. Wazero is an open-source WebAssembly runtime written in Go, and it lets a Go application load a .wasm module, call the functions it exports, and hand it host functions written in Go. This wazero webassembly go tutorial follows the full arc in one guide: install it, build a real module with TinyGo, embed and call it, pass strings in and out, add host functions, and run modules with no Go code at all through the WASI CLI.

To use wazero, install it with go get github.com/tetratelabs/wazero@latest, create a runtime with wazero.NewRuntime(ctx), instantiate the WASI host module, load your .wasm file with InstantiateWithConfig, and call an exported function with mod.ExportedFunction("add").Call(ctx, 1, 2). You need no CGO, no shared libraries, and no separate runtime process, because wazero runs inside your Go binary as a plain library. The whole first program fits in seven steps, and the sections below walk through them in order.

All facts in this guide are as of November 2026. CodexFuse offers no Wazero tool, so this guide teaches the open-source project itself, step by step, with every version, command, and limit taken from the official wazero homepage, the GitHub repository, the pkg.go.dev package docs, and the TinyGo README.

What Is Wazero, and How Do You Use It?

Wazero is a WebAssembly runtime written in Go that embeds inside your application instead of running as a separate process. The homepage calls it the only zero dependency WebAssembly runtime written in Go, and the README adds the qualification that matters: wazero has no dependencies except Go and golang.org/x/sys. That single dependency is the reason it stays small and portable, and it is also the reason the zero-dependency headline needs its qualifier stated once, here, so nobody is surprised when go.mod gains one require line.

The mental model has three parts, and each one maps to a single API call. A Runtime compiles binaries, configures host functions, and runs guests in sandboxes. A CompiledModule is the decoded, validated, and possibly compiled form of a .wasm binary.

A Module is that compiled module instantiated and running, with its exported functions callable from Go. You create the runtime, you instantiate the module, you call the function, and you close the runtime when you are done.

WebAssembly itself is a way to safely run code compiled in other languages, and wazero is the Go-native way to host it. A TinyGo program, a Rust program, or a Zig program can all compile to the same .wasm format, and the same Go host can run any of them. That is the whole point of the plugin story: your Go application stays Go, and the behaviour that changes often ships as a small sandboxed module you can swap without rebuilding the host.

The project is 2,377 days old, counted from its repository creation on 4 May 2020 to today, 6 November 2026. It reached 1.0 in March 2023, which is 44 months ago, and the current release is v1.12.0, published 29 May 2026, which is 161 days ago. The repository holds 6,454 stars and 373 forks as of 6 November 2026, which works out to 17.3 stars per fork and a 5.8 percent fork ratio, across 35 releases and 1,989 commits, or 56.8 commits per release.

Wazero is a registered trademark of Tetrate, Inc., and the project is released under the Apache-2.0 license. The repository recently moved from the tetratelabs organization to the wazero organization, so release pages now live under github.com/wazero/wazero, while the Go import path stays github.com/tetratelabs/wazero. Keep the import path exactly as written in the code below, because the module path did not move with the repository.

The companion for the container side of this guide is our Docker beginner guide, because the finished pattern is usually a Go host in a container running plugin modules that arrive as files. The rest of this guide stays inside Go and WebAssembly, and the Docker guide covers the shipping half.

What Do You Need Before You Start?

You need a Go toolchain, a TinyGo toolchain, and a terminal, and nothing else is required before the install section. TinyGo is the compiler that turns Go source into a .wasm file, because the standard Go compiler targets the browser port, not the WASI port wazero hosts. The current TinyGo release is v0.44.0, published 28 October 2026, which is 9 days ago, so install it fresh rather than trusting a version from last year.

The table below pins every version this guide uses, so a reader in six months can tell exactly which release the commands were tested against. Pinning matters here because the TinyGo target name changed recently, and an old tutorial that still says wasi will fail on the current toolchain. The version column is the whole point of the table: copy it, do not guess it.

ToolVersion pinned in this guideWhat it is for
wazerov1.12.0 (29 May 2026)The WebAssembly runtime library your Go host imports
TinyGov0.44.0 (28 October 2026)Compiles Go source into a .wasm module for wazero to run
Go toolchainA recent stable releaseBuilds the host program and fetches the wazero module
TerminalAny Unix shellRuns go get, tinygo build, and the wazero CLI

Check both versions before you write any code, because a stale TinyGo is the single most common cause of the errors in this guide. Run tinygo version and confirm it prints 0.44.0, then run go version and confirm a recent stable release. If TinyGo is older, upgrade it first: the wasip1 target name in the next sections only exists on current releases.

You also need a project directory with a go.mod file, which go mod init wazero-demo creates for you. The examples in this guide use a directory called wazero-demo, and every command assumes you are inside it. Keep the guest source and the host source in the same directory while you learn, and split them into separate modules only once the pattern is familiar.

How Do You Install Wazero?

Install wazero with one go get command, and pin the version while you are at it. The latest release is v1.12.0, and the command below fetches it into your module. The homepage and the README both print the same command, so there is no ambiguity about the install route.

go get github.com/tetratelabs/wazero@latest
go list -m github.com/tetratelabs/wazero

The second command prints the version go resolved, and you should see v1.12.0 before you continue. If it prints anything else, your module cache or a replace directive is holding an older release, and you should pin explicitly with go get github.com/tetratelabs/[email protected]. Pinning is the habit that keeps a tutorial reproducible: latest moves, a version number does not.

The release history shows why the pin is worth stating. Version 1.11.0 arrived on 19 December 2025, which is 322 days ago, and v1.12.0 followed 161 days later on 29 May 2026.

The gap before that was much shorter: v1.10.1 landed on 11 November 2025, only 38 days before v1.11.0. Releases do not arrive on a fixed schedule, so a guide that says latest without a number is already drifting the day it is published.

ReleasePublishedDays agoWhat changed
v1.12.029 May 2026161Steps toward Wasm 3.0: extended constant expressions, exception handling, typed references, non-blocking IO on custom files
v1.11.019 December 2025322Previous release in the 1.x line
v1.10.111 November 2025360Patch release, 38 days before v1.11.0
v1.9.019 February 2025625Earlier 1.x release
v1.8.225 November 2024711Earlier 1.x release

One version number you will see in other projects is v1.11.0, and it is not wazero's recommendation. The QIP project's docs pin v1.11.0 for their own reproducibility, and that pin is theirs, not the runtime's. This guide pins v1.12.0 throughout, because it is the latest release the GitHub Releases API reports as of 6 November 2026.

The install also pulls in golang.org/x/sys, which is the single dependency the README's support policy names. That is the whole dependency tree: your host binary grows by wazero plus x/sys, and nothing else.

By avoiding CGO, wazero avoids prerequisites such as shared libraries or libc, and it keeps features like cross compilation working. The releases live on GitHub, and our GitHub beginner guide covers cloning the repository and tracking new releases, so your pin stays current.

How Do You Build Your First WebAssembly Module With TinyGo?

Build your first module from a tiny Go program that exports one function, because the smallest possible guest teaches the whole pipeline. The official basic example uses an add function that takes two numbers and returns their sum, and this wazero tinygo section follows that exact example. The guest file is four lines of Go, and the build command is one line of TinyGo.

// add.go — the guest module, compiled to add.wasm
package main

//export add
func add(x, y uint32) uint32 {
	return x + y
}

func main() {}

Compile it with TinyGo, targeting wasip1, and write the output to add.wasm. The target name is the detail that breaks most first attempts, so read it carefully: the current target is wasip1, not wasi.

The old wasi spelling still appears in the pkg.go.dev example comment, in the allocation examples README, and on the wazero.io languages page, but those pages are stale on this point. The current main-branch basic example README and the current TinyGo README both say wasip1, and this guide uses wasip1 everywhere.

tinygo build -buildmode=c-shared -target=wasip1 -o add.wasm add.go

The -buildmode=c-shared flag matters, because it tells TinyGo to export the add function from the module instead of building a standalone program. Without it, the function is not exported, and the host's call to ExportedFunction("add") finds nothing. If you use Go 1.24 or newer, the equivalent form is GOOS=wasip1 GOARCH=wasm tinygo build -buildmode=c-shared -o add.wasm add.go, which says the same thing through environment variables instead of the -target flag.

The main function in the guest is empty on purpose, and that emptiness is a TinyGo convention, not a mistake. TinyGo needs a main package and a main function to build the module, but the module's real entry point for wazero is the _initialize start function, which the host configures in the next section. Keep main empty and let the exported functions do the work.

Verify the module exists before you write the host, because a missing .wasm file produces a confusing error later. Run ls -la add.wasm and confirm the file is there, then run file add.wasm if your system has the file command, which should identify it as a WebAssembly binary. A build that silently failed is worse than a build that loudly failed, and the listing takes two seconds.

How Do You Embed and Call WebAssembly From Go in 7 Steps?

Embedding is the heart of this wazero tutorial, and it takes seven steps from an empty host file to a printed answer. The example below is the official basic example from the wazero repository, with the add.wasm module from the previous section loaded from an embedded byte slice. Run the code as written first, then read the steps to understand what each line did.

  1. Create a runtime with wazero.NewRuntime(ctx), which uses the compiler engine by default when the platform supports it.
  2. Instantiate the WASI host module with wasi_snapshot_preview1.MustInstantiate(ctx, r), which gives the TinyGo guest the imports it needs, including the ones behind panic.
  3. Load the module bytes with go:embed, so the .wasm file travels inside the host binary.
  4. Instantiate the guest with InstantiateWithConfig, passing a module config whose start function is _initialize.
  5. Look up the exported function with mod.ExportedFunction("add").
  6. Call it with .Call(ctx, 1, 2) and read the first result value.
  7. Print the answer and close the runtime with defer r.Close(ctx).
package main

import (
	"context"
	_ "embed"
	"fmt"
	"log"

	"github.com/tetratelabs/wazero"
	"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
)

//go:embed add.wasm
var addWasm []byte

func main() {
	ctx := context.Background()
	r := wazero.NewRuntime(ctx)
	defer r.Close(ctx)

	wasi_snapshot_preview1.MustInstantiate(ctx, r)

	mod, err := r.InstantiateWithConfig(ctx, addWasm,
		wazero.NewModuleConfig().WithStartFunctions("_initialize"))
	if err != nil {
		log.Panicln(err)
	}

	add := mod.ExportedFunction("add")
	results, err := add.Call(ctx, 1, 2)
	if err != nil {
		log.Panicln(err)
	}
	fmt.Printf("1 + 2 = %d\n", results[0])
}

Run it with go run main.go and expect exactly one line of output: 1 + 2 = 3. The repository's own example runs the same code with 7 and 9 and prints 7 + 9 = 16, which confirms the function really executes inside the guest and the numbers are not hard-coded. Change the arguments in your copy, run it again, and watch the answer change: that is the moment the embedding stops being theory.

The WithStartFunctions("_initialize") call deserves one paragraph of its own, because it is the line beginners delete first. TinyGo's wasip1 modules run an initialization function when they start, and the host must name it, or the guest never finishes setting itself up.

The default start function is _start, and WithStartFunctions overrides that default with _initialize for TinyGo guests. Call WithStartFunctions with no arguments only when you want to clear the default entirely, which you do not want here.

The import path for the WASI host module is github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1, and the MustInstantiate call is the whole setup. That one line registers every WASI preview1 host function the guest can import, and the guest's calls to those functions are what let TinyGo implement panic, print, and file access. Without it, instantiating a TinyGo module fails on a missing import, and the error names the import it could not find.

Keep the host and the guest in version control together, because the .wasm file is a build artifact of the guest source. Our Git beginner guide covers the repository layout, and the habit to take from it is simple: commit add.go, rebuild add.wasm in CI, and never hand-edit a .wasm file. A module you cannot rebuild from source is a module you cannot trust.

How Do You Pass Strings In and Out of WebAssembly?

Passing numbers is easy, because a number fits in a single Wasm value, but passing a string is the first real problem every wazero user meets. WebAssembly has no string type and no shared memory allocator, so the host and the guest must agree on where the bytes live and who frees them. The repository's allocation examples exist to teach exactly that agreement, and they work for binary serialization, not just for strings.

The example to run is greet.go from the allocation examples, which calls a guest function that returns a greeting and prints it twice: once from inside the guest, once from the host. The command and its output are short, and the output is the whole lesson in two lines.

$ go run greet.go wazero
wasm >> Hello, wazero!
go >> Hello, wazero!

The two lines print the same greeting because the same bytes cross the boundary in both directions: the guest builds the string in its own memory, and the host reads it out of that memory. The host cannot simply take a Go string and hand it to the guest, because the guest has no access to Go memory. Instead, the guest allocates space, the host writes the bytes into the guest's memory, the guest reads them, and the guest's own allocation function hands memory back for the return trip.

There is no WebAssembly specification for allocation, which is the sentence the allocation README states plainly, and it is the reason every language does this differently. TinyGo exports allocation functions the host can call, while Rust and Zig do not, and Rust eagerly collects memory before returning from a Wasm function while TinyGo does not. Those differences mean the string-passing code you write for a TinyGo guest will not work unchanged for a Rust guest, and the allocation examples directory exists so you can read the per-language variant you actually need.

The three language variants in the examples are Rust, TinyGo, and Zig, and each one builds with its own toolchain. The Rust variant builds with cargo build --release --target wasm32-unknown-unknown, the TinyGo variant builds with tinygo build -o X.wasm -scheduler=none --no-debug -target=wasi X.go, and the Zig variant builds with zig build.

Read the TinyGo command with care: the allocation README still spells the target as wasi, which is the legacy name this guide replaces with wasip1 everywhere else. The build flags are the point, not the spelling: -scheduler=none and --no-debug keep the module small and the memory layout predictable.

The rule to carry out of this section is one line long: whoever allocates the memory owns freeing it, and the boundary crossing must be explicit on both sides. A host that writes into guest memory it did not allocate corrupts the guest, and a guest that returns a pointer the host never frees leaks memory on every call. Write the allocation helpers once, test them with the greet example, and reuse them everywhere a string crosses the boundary.

How Do You Add Wazero Host Functions That WebAssembly Can Call?

Host functions run the boundary in the other direction: the guest calls into the host, and Go code answers. A host function is an ordinary Go function that you register under a module name, and the guest imports it the same way it imports a WASI function. The pattern is three calls long, and the pkg.go.dev docs print it as the canonical example.

_, err := r.NewHostModuleBuilder("env").
	NewFunctionBuilder().WithFunc(hello).Export("hello").
	Instantiate(ctx, r)

Read the chain from left to right and it explains itself. NewHostModuleBuilder("env") creates a host module named env, NewFunctionBuilder starts defining one function, WithFunc(hello) wraps your Go function, Export("hello") names it for the guest, and Instantiate registers the whole module with the runtime. The builder is mutable, so each method returns the same instance for chaining, and validation errors are deferred until Compile, which means a bad signature surfaces at instantiation time rather than at registration time.

The import-go example in the repository shows the same pattern doing real work: it defines Go functions, including one that returns the current year and one that prints to the console, and imports them into a Wasm module that calls them. The guest module is written in Wasm text format, and its import section names the env module and the function names the host registered. The two sides meet on names alone: if the guest imports env.hello and the host exported hello from env, the call works, and if either name differs by one character, instantiation fails.

Two API details from the docs decide how you write host functions in practice. First, WithFunc uses reflection to map a Go func to a WebAssembly function, so the Go signature must use types the mapping understands, such as uint32 and string-adjacent values.

Second, direct use of ExportedFunction is forbidden for host modules, because host modules are not guests: you call them from Wasm, never from Go. The host calls the guest, the guest calls the host, and each direction uses its own API.

The deeper idea is that host functions are the API your guest programs against, and our what an API is guide explains that contract from the caller's side. Design the host functions first, name them plainly, keep their signatures narrow, and document them as the guest's API. A plugin system whose host functions are an accident of whatever the host happened to export is a plugin system nobody can write a guest for.

How Do You Run WebAssembly With the Wazero CLI?

The wazero CLI runs a .wasm file without any host code at all, which makes it the fastest way to test a module you just built. Install it with go install, then point it at the module and pass arguments after the file name. The CLI is a thin host: it creates a runtime, instantiates WASI, runs the module, and wires up stdin, stdout, and stderr.

go install github.com/tetratelabs/wazero/cmd/wazero@latest
wazero run calc.wasm 1 + 2

The command accepts a single argument, the path to the WebAssembly binary, and arguments can be passed to the binary itself after the path. In the example above, calc.wasm receives 1, +, and 2 as its own arguments, exactly as if you had run a native binary with those arguments. The module also has access to stdout, stderr, and stdin, so a guest that prints or reads behaves the same way under the CLI as it does under your Go host.

The homepage offers a second install route, an install script, for machines where go install is not the workflow you want. The script downloads a release binary into a local bin directory, and you run it from there. Both routes install the same CLI, so pick the one that matches how you install tools and move on.

curl https://wazero.io/install.sh | sh
./bin/wazero run app.wasm

Use the CLI as your smoke test, not as your production host. Build the module with TinyGo, run it with wazero run, confirm the output, and only then write the Go host that embeds it.

That order catches build errors in seconds, because the CLI fails fast on a bad module, while a host program fails with the same error wrapped in your own code. The CLI is also the tool to hand to a teammate who needs to run a module but does not need to read your host source.

An AI coding agent can drive this CLI the same way you do, which is why our Claude Code beginner guide pairs naturally with this section. The agent runs tinygo build, then wazero run, reads the output, and fixes the guest source in a loop, and the CLI is the feedback step that makes the loop work. Keep the loop tight: build, run, read, fix, and never let the agent edit a .wasm file directly.

How Does WASI Keep the Guest Sandboxed?

WASI is the system interface the guest uses to talk to the outside world, and wazero's WASI support is preview1, under the module name wasi_snapshot_preview1. A TinyGo guest imports WASI functions for printing, exiting, reading the clock, and opening files, and the host module you instantiated in the first program provides them. Without that host module, the guest's imports have nothing to resolve against, and instantiation fails.

The sandbox is not a single wall but a set of defaults, and the pkg.go.dev ModuleConfig docs state each default with the same warning: inheriting the host value would violate sandboxing. Nothing crosses the boundary unless the host configures it, which is the design that makes wazero safe to run untrusted modules with. The table below lists the defaults, because a plugin author who knows them can predict exactly what a guest can and cannot do.

ResourceDefault for the guestHow the host grants it
Standard inputEnd of file, nothing to readWithStdin, passing a reader
Standard output and errorDiscarded silentlyWithStdout and WithStderr, passing writers
ArgumentsNone, not the host's os.ArgsWithArgs, passing the argument list
Environment variablesNone, not the host's process environmentWithEnv, passing one key and value at a time
Wall clockA fake clock increasing 1ms per readWithSysWalltime, opting into the real clock
Random numbersDeterministic, the same sequence every runWithRandSource, passing crypto/rand.Reader
File systemNo access, path_open returns ENOSYSWithFSConfig, mounting a directory

The file system default is the one to understand best, because it is the most surprising. By default, no file access is allowed, so a guest that tries to open a file gets an unsupported error.

The host grants access with WithFS, which mounts a file system at the guest root, or with WithFSConfig, which mounts a directory at a guest path, read-only or read-write. The guest will have full access to the mounted directory, including escaping it through relative lookups such as ../../, so mount the smallest directory the guest needs and prefer the read-only mount whenever the guest only reads.

The clock and random defaults deserve the same attention, because they exist for reproducibility. A guest that reads the wall clock gets a fake clock that advances 1 millisecond per read, and a guest that asks for random bytes gets a deterministic sequence, so two runs of the same module with the same inputs produce the same outputs. Opt into the real clock with WithSysWalltime and into real randomness with WithRandSource only when the guest genuinely needs them, and expect your tests to become non-deterministic the moment you do.

ModuleConfig is immutable, which is a small API fact with a large practical consequence: each WithXXX method returns a new config instead of changing the one you have. Chain the calls in one expression, as the examples do, and do not call a With method and then pass the original config expecting the change to be there. The same discipline applies to the runtime config, which you build once with NewRuntimeConfig and pass to NewRuntimeWithConfig.

Compiler or Interpreter: Which Wazero Engine Should You Pick?

Wazero ships two engines, and the runtime picks the compiler by default when the platform supports it. The compiler translates the module into machine code ahead of time, during CompileModule, so the guest's functions execute natively at run time. The interpreter is a naive interpreter-based implementation of the Wasm virtual machine, and it runs the module instruction by instruction without generating machine code.

The official speed claim compares the two engines against each other, and it is the only performance number this guide states. The compiler is faster than the interpreter, often by an order of magnitude (10x) or more, according to the README, and it achieves that without host-specific dependencies. That 10x figure is a compiler-versus-interpreter claim inside wazero, not a claim about wazero versus any other runtime, and it should never be quoted as one.

EngineHow to select itPlatformsBest for
Compiler (default)wazero.NewRuntime(ctx)amd64 and arm64Production hosts where guest speed matters
InterpreterNewRuntimeWithConfig with NewRuntimeConfigInterpreter()amd64, arm64, and others such as riscv64Platforms the compiler does not support, and tiny hosts

Force the interpreter with one line when you need it, and the rest of your host code stays unchanged. The interpreter has no platform-specific code, so it runs on any compilation target Go supports, which is the reason it exists at all. A host that must run on riscv64, or inside an application with no operating system, has no compiler to use, and the interpreter is the engine that still works there.

r := wazero.NewRuntimeWithConfig(ctx, wazero.NewRuntimeConfigInterpreter())

compiled, err := r.CompileModule(ctx, addWasm)
if err != nil {
	log.Panicln(err)
}
mod, err := r.InstantiateModule(ctx, compiled, wazero.NewModuleConfig())
if err != nil {
	log.Panicln(err)
}

The snippet above also shows the two-phase form of the API, which is worth using whenever you instantiate the same module more than once. CompileModule turns the bytes into a CompiledModule once, and InstantiateModule creates a fresh Module from it each time, so a server that spins up a guest per request pays the compile cost once and the instantiation cost per request. Arcjet's production lessons post, published 2 October 2024, which is 765 days ago, recommends exactly this shape, alongside pre-compiling to a CompiledModule and pre-initializing the guest, to keep startup time inside its latency goals.

Both engines pass the WebAssembly Core 1.0 and 2.0 specification tests on supported platforms, so conformance is not the reason to pick one over the other. Pick the compiler when the platform allows it and the guest runs hot, pick the interpreter when the platform demands it or the host is small, and measure your own workload before arguing about either. The docs describe a future wazero 2.0 optimizing compiler built on SSA, but the latest released version is 1.12.0, so do not present that optimizing compiler as shipped.

Wazero vs Wasmtime: Which Runtime Fits Your Project?

Wazero vs wasmtime is the comparison every Go developer asks, and the honest answer starts with what each runtime is for. Wasmtime is the standalone runtime from the Bytecode Alliance, built on the Cranelift code generator, and it is the reference implementation for many WASI standards, with full Component Model support and Go bindings under wasmtime-go. Wazero is a pure Go library that embeds into your application with no CGO and no native libraries, and it is the runtime you pick when the host is Go and the module is a plugin.

The differentiator the README states is the embedding story, not speed. Wazero has no dependencies and does not require CGO, which means it can be embedded in an application that does not use an operating system, and the project verifies the zero-dependency claim by running its tests in Docker's scratch image.

Wasmtime, by contrast, is a standalone runtime with its own CLI and embedding story in several languages, and its Go bindings wrap a native library. A Go team that wants the runtime inside the binary picks wazero; a team that wants the reference runtime with the broadest WASI coverage picks wasmtime.

QuestionWazeroWasmtime
What is itA Go library that embeds in your applicationA standalone runtime from the Bytecode Alliance
Latest releasev1.12.0, 29 May 2026v50.0.1, 29 October 2026, which is 8 days ago
Code generatorCompiler (AOT) plus an interpreterCranelift JIT
CGO or native librariesNone, pure Go plus x/sysNative library behind the Go bindings
WASI supportPreview 1 (wasi_snapshot_preview1)Preview 2 and the Component Model
Best forGo hosts embedding plugin modulesServer-side and cloud-native workloads needing the newest WASI standards

The version numbers in the table show how differently the two projects move. Wasmtime's v50.0.1 arrived on 29 October 2026, eight days before this guide, while wazero's v1.12.0 arrived on 29 May 2026, 161 days before it. A fast-moving major version is not better or worse than a slow-moving one: Wasmtime tracks the newest WASI standards, and wazero concentrates on high-quality support for stable specifications, as a wazero maintainer put it in a public discussion thread.

No fetched source provides a head-to-head benchmark between wazero and wasmtime, so this guide claims none. The third-party runtime listicles that rate both projects describe them loosely and are low-authority sources, and their scores are not repeated here. Choose on the embedding story and the WASI version you need, run your own workload through both, and let your numbers decide.

What Should You Watch Out For?

Four limits decide whether wazero fits your project, and each one is easy to miss if you only read the homepage. The first is the WASI version: wazero supports WASI Preview 1 through wasi_snapshot_preview1, and Preview 2 and Component Model support was not found in any official source fetched for this guide as of 6 November 2026.

Arcjet noted the missing Component Model support in July 2024, which is 836 days ago, and the v1.12.0 release notes describe progress toward Wasm 3.0 without mentioning Preview 2. If your modules need Preview 2 interfaces, verify support against the current release before you commit, and treat Preview 1 as the supported path until you find otherwise.

The second limit is the documentation's age in places. The Go Wiki WebAssembly page, which documents the Go 1.21 WASI port and the wasip1 build target, never mentions wazero by name anywhere in its text, so do not claim the Go Wiki endorses or documents wazero. The page is still useful for the Go-side facts, including that Go 1.21 added the WASI port and that the build command is GOOS=wasip1 GOARCH=wasm go build, but it is not a wazero source.

The third limit is the CLI surface. This guide documents only the wazero run form it verified from the CLI README: wazero run followed by the module path and its arguments.

Further flags were not verified from the CLI README this run, so run wazero run --help on your installed binary before documenting any flag, and do not copy flags from a tutorial that does not show its own --help output. A flag that does not exist fails loudly, but a flag that exists with different semantics fails silently, and the second failure is worse.

The fourth limit is the TinyGo wasip2 target, which exists in TinyGo but whose compatibility with wazero was not verified against TinyGo's own current docs this run. TinyGo lists wasip2 as a target, and a WasmCon24 deck describes native WASI 0.2 support in TinyGo, but wazero has no documented wasip2 host module, so do not build a wasip2 guest and expect wazero to run it. Stay on wasip1, which is the verified path in every section above.

One production habit closes the guide, and it comes from the Arcjet lessons post and the CNCF webinar of 26 September 2024, which is 771 days ago. Pre-compile once, instantiate many times, pre-initialize the guest before the first request, and profile the boundary rather than the guest alone, because the host-to-guest call is where the overhead lives. The webinar, presented by Arcjet's David Mytton, and the Linux Foundation talk from WasmCon 2023 by Edoardo Vacchi both make the same point in different words: wazero is simple to start and easy to run slowly, and the difference between the two is the compile-once habit.

Sources

The facts in this guide come from the official wazero homepage at wazero.io, the GitHub repository at github.com/tetratelabs/wazero, the GitHub Releases API for the v1.12.0 release of 29 May 2026 and the v1.11.0, v1.10.1, v1.9.0, and v1.8.2 history, and the pkg.go.dev package documentation for the wazero package, the ModuleConfig methods, and the HostModuleBuilder. The TinyGo facts come from the TinyGo README on the release branch and the GitHub Releases API for TinyGo v0.44.0 of 28 October 2026, and the Wasmtime version comes from the GitHub Releases API for v50.0.1 of 29 October 2026.

The worked examples come from the repository's examples directory: the basic example with its TinyGo build command and 7 + 9 = 16 output, the allocation examples with the greet.go output and the Rust, TinyGo, and Zig build commands, the import-go example with its host-function pattern, and the cmd/wazero directory with the CLI install and run commands. The Go Wiki WebAssembly page at go.dev supplied the Go 1.21 WASI history, and the Go blog post on WASI supplied the wasip1 port introduction.

The independent production material comes from Arcjet: the lessons-from-production post by David Mytton of 2 October 2024 and the Rust-to-Wasm post of 23 July 2024, plus the CNCF on-demand webinar listing for the 26 September 2024 session and the Linux Foundation WasmCon 2023 talk slides by Edoardo Vacchi. The QIP docs supplied the v1.11.0 pin as a third-party example, the WASI 0.2 explainers from Reintech and Redline Software supplied the Preview 2 background, and the Xe Iaso lightning talk of 24 March 2023 supplied an early independent account of gluing Rust-compiled Wasm into a Go program with wazero. Every number in this guide was recomputed from the fetched dates and counts in the Python block below, and no statistic, price, or benchmark was taken from memory.

from datetime import date
today = date(2026, 11, 6)
repo_age = (today - date(2020, 5, 4)).days
v112_age = (today - date(2026, 5, 29)).days
v111_age = (today - date(2025, 12, 19)).days
gap_111_112 = (date(2026, 5, 29) - date(2025, 12, 19)).days
gap_1101_111 = (date(2025, 12, 19) - date(2025, 11, 11)).days
v1101_age = (today - date(2025, 11, 11)).days
v19_age = (today - date(2025, 2, 19)).days
v182_age = (today - date(2024, 11, 25)).days
tinygo_age = (today - date(2026, 10, 28)).days
wasmtime_age = (today - date(2026, 10, 29)).days
arcjet_lessons = (today - date(2024, 10, 2)).days
arcjet_rust = (today - date(2024, 7, 23)).days
cncf_webinar = (today - date(2024, 9, 26)).days
stars, forks = 6454, 373
print(repo_age, v112_age, v111_age, gap_111_112, gap_1101_111, v1101_age, v19_age, v182_age, tinygo_age, wasmtime_age, arcjet_lessons, arcjet_rust, cncf_webinar, round(stars / forks, 1), round(forks / stars * 100, 1), (2026 - 2023) * 12 + (11 - 3), round(1989 / 35, 1))

Frequently Asked Questions

Is wazero really zero-dependency?

Almost, with one named exception. The homepage calls wazero the only zero dependency WebAssembly runtime written in Go, and the README's support policy names a single dependency beyond Go itself: golang.org/x/sys. Your module gains one require line for x/sys, and nothing else, which keeps the host binary small and cross-compilation working without a C toolchain.

Do I need TinyGo to use wazero?

No, TinyGo is the Go path, not the only path. Any language that compiles to a .wasm module works, and the repository's examples include Rust, Zig, and AssemblyScript guests alongside the TinyGo one. TinyGo is the natural choice when the guest is also written in Go, because it compiles Go source directly to a wasip1 module, but a Rust guest built with cargo and a Zig guest built with zig build run under the same host.

Can wazero run WASI Preview 2 or Component Model modules?

Not on any official source fetched for this guide. Wazero's supported WASI path is Preview 1 through wasi_snapshot_preview1, and no official source fetched for this guide documents Preview 2 or Component Model support as of 6 November 2026. If your modules need Preview 2 interfaces, check the current release before committing, and use Wasmtime, which supports them, until wazero documents otherwise.

Is wazero faster than wasmtime?

There is no published head-to-head benchmark between them in the sources fetched for this guide, so no honest answer gives a number. The only verified speed claim is wazero's own: its compiler is often an order of magnitude (10x) or more faster than its own interpreter. Pick on the embedding story and the WASI version you need, then measure your own workload through both runtimes.

Can I run a .wasm file without writing any Go code?

Yes, use the wazero CLI. Install it with go install github.com/tetratelabs/wazero/cmd/wazero@latest, then run wazero run calc.wasm 1 + 2, passing the module's own arguments after the file name. The module gets stdin, stdout, and stderr, so it behaves the same way under the CLI as it does under a Go host, which makes the CLI the fastest smoke test for any module you build.

Featured on Twelve Tools Hunt for Tools Featured on LaunchBuff Launch Llama Tools Launch your MCP Top Web Development Companies in the US - OnToplist.com Designer Listings Listed on SeeWhatNewAI Featured on SubmitMySaas Featured on AI Agents Directory Product Wing SaaS Wheel