Can WebAssembly Run on RISC-V? Testing Atym Containers on a Banana Pi F3

Can WebAssembly Run on RISC-V? Testing Atym Containers on a Banana Pi F3

Photo by Thibaut Tattevin on Pexels

In my previous article, I built WebAssembly containers with Atym on x86 and got application binaries under 23 KB. The whole time, a question kept nagging me: if WebAssembly is truly architecture-portable, can I take the exact same .wasm binary, copy it to a RISC-V board, and have it just work?

I have a Banana Pi F3 sitting on my desk. SpacemiT K1 processor, rv64gc, 16 GB of RAM, running Armbian Debian Trixie. I built the first Docker Engine v29 for RISC-V on a board like this. Time to see if WebAssembly keeps its promises where Docker took months of effort.

The setup

The Banana Pi F3 is not a toy. It’s a proper RISC-V single-board computer:

  • CPU: SpacemiT K1, rv64gc (8 cores)
  • RAM: 16 GB
  • Storage: 128 GB eMMC (30 GB free)
  • OS: Armbian 25.11.2 (Debian Trixie)
  • Kernel: 6.6.99-current-spacemit riscv64
  • Toolchain: GCC 14.2, cmake 3.31.6, git 2.47

Everything I need to compile C code is already there. The question is whether WAMR – the WebAssembly runtime at the heart of Atym and the Ocre project – compiles and runs on this architecture.

Level 1: building iwasm from source

WAMR (WebAssembly Micro Runtime) is the engine that executes .wasm binaries. It’s open source, maintained by the Bytecode Alliance, and lists RISC-V as a supported target. Time to find out if the code matches the claim.

git clone --depth 1 https://github.com/bytecodealliance/wasm-micro-runtime.git
cd wasm-micro-runtime/product-mini/platforms/linux
mkdir build && cd build
cmake ..
make -j4

The cmake output tells the story:

-- WAMR version: 2.4.3
-- Build Configurations:
     Build as target RISCV64
     Build for platform linux
     WAMR Interpreter enabled
     WAMR AOT enabled
     Libc WASI enabled
     SIMD disabled due to not supported on target RISCV64

WAMR auto-detects RISCV64 and pulls in the right assembly files: invokeNative_riscv.S for the native call bridge and aot_reloc_riscv.c for AOT relocations. The build runs clean. No patches, no workarounds.

Result:

$ file iwasm
iwasm: ELF 64-bit LSB pie executable, UCB RISC-V, RVC, double-float ABI,
       version 1 (SYSV), dynamically linked, for GNU/Linux 4.15.0

A native RISC-V binary, built from source in under a minute.

Level 2: the money shot

Now the real test. I have hello-world.wasm on my x86 development machine – built with WASI SDK v29 inside the Atym dev container. I did not rebuild it. I did not recompile anything. I just copied it:

# From my x86 WSL machine:
scp hello-world.wasm poddingue@192.168.1.185:~/

And ran it:

./wasm-micro-runtime/product-mini/platforms/linux/build/iwasm hello-world.wasm
     _      _________  ___   ______________  ______  ____  __
    | | /| / / __/ _ )/ _ | / __/ __/ __/  |/  / _ )/ /\ \/ /
    | |/ |/ / _// _  / __ |_\ \_\ \/ _// /|_/ / _  / /__\  /
    |__/|__/___/____/_/ |_/___/___/___/_/  /_/____/____//_/
                                           powered by Ocre

Same binary. Same output. x86 to RISC-V. No recompilation, no multi-arch buildx, no manifest wrangling. Just scp and run.

I spent weeks getting Docker images to build for riscv64: QEMU user emulation, cross-compiled base images, glibc version mismatches. This took one scp command. WebAssembly’s portability promise isn’t theoretical. It works today on real hardware.

Testing more samples

I copied over the other .wasm files I had built on x86:

health-monitor.wasm (my custom app, 22 KB):

========================================
  Device Health Monitor v1.0
  Built with Atym + Ocre SDK
  Compiled to WebAssembly
========================================
Reporting interval: 5000ms
Thresholds: CPU>70% MEM>75% TEMP>50C

warning: failed to link import function (env, ocre_timer_create)
warning: failed to link import function (env, ocre_timer_start)
Exception: failed to call unlinked import function (env, ocre_register_dispatcher)

The app loads, prints its banner, then fails when it tries to call Ocre SDK functions like ocre_timer_create. This is expected – iwasm alone doesn’t provide the Ocre API implementation. The app needs the full Ocre runtime for that. But it proves the binary is valid and the startup code runs.

echo-server.wasm was even more interesting:

**********************************************
Echo server starting...
Echo server listening on port 8000
**********************************************
To connect:
1. Find this device's IP: net iface
2. Telnet to the device: telnet <IP> 8000
**********************************************
bind: Permission denied

It gets all the way to trying to bind a network socket. The failure is a Linux permissions issue (binding port 8000), not an architecture issue. The WASI socket implementation works on RISC-V.

The gap: Atym’s commercial tooling

With iwasm working, I checked whether Atym’s own tools run on RISC-V.

Atym CLI: I added the apt repository (apt.dist.atym.io) and ran apt update. No packages for riscv64. The CLI is distributed as pre-built binaries for amd64, arm64, macOS, and Windows only.

Atym Linux Runtime: I checked the download URLs. atym-runtime-debian-aarch64.tar.gz exists. atym-runtime-debian-riscv64.tar.gz returns a 404.

This is not surprising. RISC-V is still a niche architecture for application deployment, and shipping binary packages for it represents a real maintenance cost. But it confirms a gap: the underlying WebAssembly technology works on RISC-V, while the commercial platform doesn’t reach there yet.

The surprise: building the Ocre runtime from source

Here’s where things got unexpected.

The Atym platform is built on Ocre, which is open source under the Linux Foundation (Apache 2.0 license). The Ocre runtime includes WAMR plus the container lifecycle management, timer system, messaging, and the shell interface for managing running containers.

I cloned it on the Banana Pi F3 and tried building:

git clone --recursive https://github.com/project-ocre/ocre-runtime.git
cd ocre-runtime
mkdir build && cd build
cmake ..
make -j4

The first attempt failed at 58% – not because of RISC-V incompatibility, but because the build system tried to cross-compile sample .wasm files using WASI SDK, which doesn’t ship RISC-V host binaries. The native C code compiled without issues.

One line change in CMakeLists.txt (commenting out the demo containers include) and a clean rebuild:

[100%] Built target ocre_cmd

Three binaries, all native RISC-V:

Binary Size Purpose
ocre_mini 606 KB Minimal runtime, runs a single embedded .wasm
ocre_demo 606 KB Demo with Ocre features
ocre_cmd 629 KB Full CLI with container management (image pull/push, container create/start/stop/ps)

Running containers on RISC-V

With the Ocre runtime built, I ran hello-world.wasm through the full container lifecycle – not just raw iwasm execution, but the actual Ocre container management system:

./build/src/samples/mini/posix/ocre_mini ~/hello-world.wasm
<inf> ocre: Ocre version 0.7.0
<inf> ocre: Build information: poddingue @ Linux bananapif3-1 [...] riscv64 GNU/Linux
<inf> ocre_common: OCRE common initialized successfully
<inf> ocre_timer: Timer system initialized
<inf> ocre: Initialized 'wamr/wasip1'
<inf> container: Created container 'd6cce35' with runtime 'wamr/wasip1'
<inf> container: Started container 'd6cce35' on runtime 'wamr/wasip1'
<inf> container: Container 'd6cce35' is running

     _      _________  ___   ______________  ______  ____  __
    | | /| / / __/ _ )/ _ | / __/ __/ __/  |/  / _ )/ /\ \/ /
    | |/ |/ / _// _  / __ |_\ \_\ \/ _// /|_/ / _  / /__\  /
    |__/|__/___/____/_/ |_/___/___/___/_/  /_/____/____//_/
                                           powered by Ocre

<inf> container: Container 'd6cce35' exited. Result is = 0
<inf> container: Container 'd6cce35' was stopped
<inf> container: Removed container 'd6cce35'
<inf> ocre_common: OCRE common shutdown successfully

That’s not just “iwasm runs a file”. That’s a container runtime creating a container from an image, starting it, executing the WebAssembly binary inside the managed lifecycle, and cleaning up on exit. On RISC-V. From source. With the same .wasm built on x86.

I also tested ocre_cmd, the full shell-based container manager:

./build/src/samples/static_checks/posix/ocre_cmd run health-monitor.wasm
<inf> container: Selected runtime engine: wamr/wasip1
<inf> file_mmap: File '[...]/health-monitor.wasm' (size=22588) mmapped successfully
<inf> container: Created container 'd6cce35' with runtime 'wamr/wasip1'
<inf> container: Started container 'd6cce35' on runtime 'wamr/wasip1'
========================================
  Device Health Monitor v1.0
  Built with Atym + Ocre SDK
  Compiled to WebAssembly
========================================
Reporting interval: 5000ms
Thresholds: CPU>70% MEM>75% TEMP>50C

<err> ocre_common: Module context not found for 0x3fb0000c70
Error: failed to register timer callback

The health monitor loads, starts, and prints its configuration. The timer callback fails because of a module context setup detail in how ocre_cmd handles the runtime lifecycle – a plumbing issue, not an architecture limitation. The .wasm itself is valid and the container machinery works.

What doesn’t work (yet)

Let me be clear about the gaps:

Component RISC-V Status Why
WAMR / iwasm Works Builds from source, first-class RISC-V support
.wasm portability Works Same binary runs on x86, ARM, and RISC-V
Ocre runtime Works Builds from source (minor CMake patch to skip sample cross-compilation)
WASI SDK Missing No rv64 host binary. Cannot compile C-to-Wasm natively on RISC-V. Cross-compile on x86/ARM instead.
Atym CLI Missing Closed-source, no rv64 package
Atym Linux Runtime Missing No rv64 binary download
Atym Hub connectivity Unknown Cannot test without Atym CLI/runtime on the board

The pattern: the open-source foundation works. The commercial layer doesn’t ship RISC-V binaries yet. Docker’s ARM support followed the same trajectory. The binaries came after the community proved demand.

The WASI SDK question

You might ask: can we build WASI SDK for RISC-V and close the loop entirely?

WASI SDK is essentially a pre-built LLVM/Clang toolchain configured to target WebAssembly. Building it from source means building the entire LLVM project, which takes hours even on powerful hardware. It ships for six host platforms (x86_64 and arm64 on Linux, macOS, and Windows) but not RISC-V.

In practice, this isn’t a blocker. WASI SDK is a cross-compiler – it runs on the host and produces architecture-independent .wasm output. The workflow is:

  1. Compile your C code on x86 or ARM (where WASI SDK exists)
  2. scp the .wasm to RISC-V
  3. Run it with iwasm or the Ocre runtime

This is the same workflow you’d use for any cross-compilation scenario. The .wasm doesn’t care which architecture produced it. That’s the whole point.

What this means

The architecture portability is real. Not in a demo, not in a slide deck. On a real RISC-V board, with a .wasm built on x86, without buildx or QEMU or multi-arch manifests. The binary just runs.

The RISC-V toolchain situation has improved faster than I expected. Two years ago, getting a C project to build on RISC-V was an adventure. WAMR and the entire Ocre runtime now build clean with GCC 14 on Armbian Debian Trixie.

The commercial Atym tooling doesn’t reach RISC-V yet, but the open-source Ocre runtime does. If you’re willing to build from source, you can run WebAssembly containers on RISC-V today. Cross-compile your .wasm on x86, deploy to RISC-V. When WASI SDK eventually ships rv64 host builds, you’ll be able to compile natively too, but you don’t need to wait.

For my own work, this changes the equation. Instead of spending weeks building Docker images for a new architecture, I can build one .wasm and deploy it everywhere. Per-architecture build matrices might not be necessary much longer, at least for the kind of embedded applications Atym and Ocre target.


Bruno Verachten is a Docker Captain, Jenkins contributor, and Arm Ambassador who has spent twelve years running containers on devices most people wouldn’t try. He teaches at Universite d’Artois and maintains 37 Docker images across ARM, x86, and RISC-V architectures.