blog.robur.coop

The Robur cooperative blog.
Back to index

Migrating Mollymawk to mkernel, mnet, and vif

2026-10-30

Mollymawk is a web application — itself running as a unikernel and designed to simplify deploying, monitoring, and managing unikernels via Albatross and reproducible builds from builds.robur.coop.

In our previous articles, we wrote about building a test suite and FreeBSD CI for mollymawk and refactoring how mollymawk structures and persists its data. At the end of that work, we mentioned the next big step: moving Mollymawk from MirageOS and Lwt to our new OCaml 5 unikernel ecosystem (uniker.nl), built around Miou, mkernel, mnet, and vif.

In this article, we cover why we migrated, what changed across the application, and some bugs we uncovered along the way.

1. The Problem

While MirageOS helped us build the initial version of mollymawk, maintaining a growing web application on top of it came with a few recurring pain points:

2. Migrating to the New Stack

Our new approach to building unikernels with OCaml 5 focuses on keeping the developer in control of the build workflow and writing direct-style code using effects rather than monadic promises or pervasive functors.

Simpler Builds with Dune, unic, and mfetch

Instead of generating build files with the mirage tool, mollymawk now builds directly with dune. Because the Solo5 OCaml compiler produces the same OCaml artifacts as the host compiler, we only need to cross-compile packages that contain C stubs.

We use unic to identify which dependencies need to be cross-compiled and mfetch to vendor them. For mollymawk, our dune workspace defines two build contexts:

Static assets (CSS, JavaScript, and images) are bundled at build time using mcrunch, which now also generates ETag checksums so browsers can cache static files instead of re-downloading them on every visit.

Direct-Style Networking and Storage with mkernel and mnet

With Miou and mkernel, we replaced all of the functorized Mirage libraries with direct-style equivalents:

Restructuring the Web Application with vif and HTMX

We replaced the nearly 4,000+ line monolithic entrypoint with vif and split the codebase into clear responsibilities:

Overall, the migration removed roughly 6,700 net lines of code across the repository while keeping all 66 routes and our native test suite intact.

Managing Dependencies Directly in OPAM

Previously, our dependencies and version constraints were split between the Mirage configuration file (config.ml) and the mirage tool itself. Whenever an upstream library introduced a breaking API change, updating or constraining versions often meant waiting on changes in the mirage tool. With our new stack, all dependencies and version bounds live directly in a standard OPAM file, giving us more flexibility when upstream libraries release updates and making local development, CI, and reproducible builds much simpler.

3. Some bugs Found Along the Way

Migrating the stack and testing the unikernel end-to-end helped us catch several bugs, both in mollymawk and in our upstream libraries:

4. Conclusion

Our production version of Mollymawk now runs entirely on our new OCaml 5 unikernel stack (Miou, mkernel, mnet, and vif). You can check it out at mollymawk.robur.coop. Moving away from multi-stage code generation, functors, and Lwt has made the codebase significantly smaller, easier to read, and simpler to build and test while exercising our new libraries in a real-world application and helping us improve them upstream.

To learn more about this new stack and our approach to developing and deploying unikernels in OCaml 5, check out our announcement on OCaml Discuss and visit our new website at https://uniker.nl.

The new mollymawk is being developed on our branch: https://git.robur.coop/robur/mollymawk/src/branch/init_mkernel and being built on our reproducible builds platform: builds.robur.coop. A draft PR to track changes and prepare merging into the main branch is open at https://git.robur.coop/robur/mollymawk/pulls/50.

Some issues we opened while working on this: