Ruvy: A Toolchain for Pre-Initialized Ruby in WebAssembly
Shopify has open sourced Ruvy, a toolchain that compiles Ruby code into a WebAssembly (Wasm) module. The module executes the original Ruby script when its exported _start function is called. Ruvy builds on top of ruby.wasm but adds two significant improvements: pre-initializing the Ruby virtual machine at build time and eliminating the need for WASI arguments at runtime.
WASI (WebAssembly System Interface) is a standardized set of imported functions that provides Wasm modules with access to system-level operations like file reads, environment variables, and time retrieval. WASI arguments are conceptually similar to command-line arguments: code compiled to WASI reads them through the WASI API, just as native code would read argv.
Building and Running a Module
Ruvy does not currently ship precompiled binaries, so users must install its build dependencies and compile it first. Instructions are in the project's README. Once built, invocation is straightforward:
The input file ruby_examples/hello_world.rb simply contains puts "Hello world". The CLI produces index.wasm, which prints that string when its _start function is executed.
For programs that span multiple Ruby files, Ruvy accepts a --preload flag pointing to a directory of additional files. Every file in that directory is loaded into the Ruby VM at build time, making its definitions available to the main script. This means the Wasm module is fully self-contained and does not need a separate filesystem at runtime.
How Ruvy Differs from ruby.wasm
ruby.wasm is a collection of CRuby ports to WebAssembly, targeting both browser environments via Emscripten and non-browser environments via WASI. The WASI ports include the Ruby interpreter itself compiled to a Wasm module, but for practical use the interpreter needs access to a filesystem to load Ruby scripts.
One common approach uses wasi-vfs, a tool that packs specified directories into the Wasm module at build time. This avoids shipping Ruby files separately, but the resulting module still expects the path to the entry script as a WASI argument. A typical invocation with a runtime like Wasmtime looks like wasmtime run module.wasm -- /src/my_app.rb.
Pre-initialization Performance Gains
The key architectural difference is when the Ruby VM starts. With a ruby.wasm module built via wasi-vfs, the VM initializes at runtime, when the module executes. Ruvy instead pre-initializes the VM during the Wasm build itself, producing a module that starts faster. Reported benchmarks show around a 20% improvement in instantiation and _start execution time using Wasmtime:
|
Description |
Toolchain |
Low |
Mid |
High |
|
Hello world |
Ruby.wasm + wasi-vfs |
55.833 ms |
56.262 ms |
56.730 ms |
|
Ruvy |
44.367 ms |
44.543 ms |
44.739 ms |
|
|
Includes + logic |
Ruby.wasm + wasi-vfs |
56.081 ms |
56.487 ms |
56.932 ms |
|
Ruvy |
44.449 ms |
44.763 ms |
45.216 ms |
The "Hello world" case is a trivial puts; the "Includes + logic" case involves a required file defining a class that transforms some input. Ruvy also reduces Wasm-to-native compilation time under Cranelift by approximately 70%:
|
Description |
Toolchain |
Low |
Mid |
High |
|
Hello world |
Ruby.wasm + wasi-vfs |
1.6351 s |
1.6590 s |
1.6844 s |
|
Ruvy |
439.93 ms |
446.31 ms |
452.81 ms |
|
|
Includes + logic |
Ruby.wasm + wasi-vfs |
1.6227 s |
1.6460 s |
1.6706 s |
|
Ruvy |
442.83 ms |
449.40 ms |
456.39 ms |
These compilation savings stem from the smaller, pre-processed state of the Ruvy module.
No Runtime Arguments Required
Because Ruvy modules carry all necessary Ruby files inside the VM snapshot, they do not require the script path as a WASI argument at execution time. This makes them usable in constrained computing environments, such as edge services, where the runtime configuration cannot supply additional WASI arguments to a _start function.
Why Open Source Ruvy
Shopify released Ruvy to give the broader developer community a simple way to build and run small Ruby programs in WebAssembly runtimes. The README lists several areas where external contributions would be welcome. In particular, Shopify Partners looking to reuse Ruby logic from Shopify Scripts within Shopify Functions may want to examine the open compatibility items for that use case.



