Skip to content

WebAssembly Is Not Just for the Browser Anymore: Running Wasm on the Server with Rust and WASI

Server-side WebAssembly with Rust and WASI is challenging containers for edge and multi-tenant workloads. I dig into the real capabilities and the hard limitations you won't see in the hype.

· 4 min read

You have seen the pitch before. WebAssembly is the future of compute. It runs everywhere. Blazing fast cold starts. Replace containers today.

Most of that is marketing translation. Here is what actually changed, and what still does not work.

Why would you run Wasm on the server at all?

The original promise of WebAssembly was secure client-side code in the browser. That sandbox model turns out to be useful server-side too, because a Wasm module gets a very narrow set of capabilities by default. No filesystem, no network, no system clocks unless you explicitly provide them via WASI (WebAssembly System Interface).

That is not a bug. It is the entire point. A standard container gives a process a huge surface area: kernel syscalls, device files, potential for container escape via a kernel bug. A Wasm runtime like Wasmtime or Wasmer gives you a virtual CPU and a table of imported functions. Nothing else. If you do not import a filesystem call, the module cannot touch the filesystem. Period.

How does that compare to containers for edge computing?

An edge function needs to start fast and die fast. Container cold starts are measured in hundreds of milliseconds to seconds for anything with a real runtime. Containers pull images, set up namespaces, run init systems, then finally execute your code.

A Wasm module is a few hundred kilobytes to a few megabytes. Wasmtime can instantiate it in single-digit milliseconds. No image pull, no overlayfs, no DNS resolution for a runtime. The module is already on the machine. You just compile it ahead of time (AOT) to native code and run it.

Cloudflare Workers and Fastly Compute@Edge run Wasm for exactly this reason. They are not selling you containers with better marketing. They are selling a fundamentally different isolation unit that can be packed thousands per machine because the overhead per module is tiny.

What does Rust bring to this?

Rust compiles to one of the leanest Wasm targets available. No garbage collector, no heavy runtime like Go or Java. A hello world Rust module compiled with cargo build --target wasm32-wasip1 is around 100 KB. The same logic in Go with TinyGo is larger and still has a runtime. Java with TeaVM is barely usable.

Rust gives you two things: size and predictable performance. No GC pauses, no JIT warmup. The module does exactly what the code says, nothing more. That predictability matters when you are billing customers per millisecond or trying to fit 10,000 tenants on a single box.

Is this actually faster than containers for multi-tenant platforms?

Faster to start, yes. Faster to execute depends on the workload. Wasmtime's AOT compilation produces near-native code for most CPU-bound tasks. The overhead is typically 5-15% compared to native. A container running the same Rust binary natively will usually be slightly faster because it does not pay the sandbox boundary cost.

But multi-tenant platforms rarely care about per-request CPU overhead as much as they care about density and blast radius. Wasm lets you put thousands of isolated tenants on one machine without each tenant needing its own OS process or container runtime. Containers done right consume maybe 20-50 MB per instance just for the runtime and libc. A Wasm module instance consumes 1-5 MB. That is a 10x density improvement before you even tune anything.

What are the real limitations today?

Here is where the hype falls apart.

Networking is still awkward

WASI preview 1 has no socket support. You cannot make an outbound HTTP request from a WASI module without the host providing a custom import. Wasmtime has a wasi-http crate that works, but it is proposal-stage and not stable across all runtimes. If your function needs to talk to a database or call another API, you are writing host-specific glue code. That kills portability, which was supposed to be the whole selling point.

Threads are essentially missing

Wasm has no shared-memory threading model. You get one thread per module unless you use experimental proposals like threads and atomics. Most server-side Rust code assumes tokio or async-std, which need threads for work-stealing and timers. You can run a single-threaded async runtime inside Wasm, but you lose parallelism. For a simple edge function that handles one request at a time, that is fine. For a throughput-optimized API server, it is a serious handicap.

No access to the host OS

This is intentional, but it trips people up. You cannot use std::fs beyond a pre-opened directory from the host. You cannot open a raw socket. You cannot spawn a subprocess. You cannot use mmap for shared memory. If you have existing Rust code that assumes normal POSIX, you will be rewriting the I/O layer to use WASI capabilities.

Debugging is harder

Stack traces from Wasm are not as good as native. You can get source maps with wasmtime and gdb, but it is not as smooth as running a binary in your local terminal. The tooling is improving, but you will spend more time staring at hex addresses than you would with a normal process.

Who is actually using this in production?

Not as many as the blog posts suggest. Cloudflare, Fastly, and Shopify are the big names. They have custom runtimes and control the whole platform. For the rest of us, running Wasm server-side means adopting a new toolchain and working around missing features.

I have not deployed Wasm in production for any of my client work. I have prototyped it for a side project that needed fast cold starts for PDF generation. Wasmtime with WASI worked, but integrating it with a Rust actix-web server meant writing a custom host wrapper. It was not turnkey.

Should you adopt it now?

If you are building an edge platform or a FaaS product, yes. The density and isolation benefits are real, and early adopters get an architectural advantage. If you are building a standard web API or a batch job, no. Containers are still the right default.

Watch for WASI preview 2 and the component model. That is where the ecosystem will converge. Sockets, async, and stable ABI will make Wasm a serious container replacement for many workloads. Until then, treat it as a niche tool with huge potential, not a drop-in replacement.

Written by

Faiz Kasman

Software engineer in Kuala Lumpur. Payments, multi-tenant SaaS, and inventory infrastructure.

Keep reading