[ANN] mnet, a new TCP/IP stack for unikernels in OCaml

I am pleased to announce a series of releases for developing unikernels with OCaml 5 and Miou. After extensive testing, we are now able to offer a new TCP/IPv4 and IPv6 stack in OCaml that aims to replace mirage-tcpip and pave the way for direct-style unikernel development. Below is an overview of the libraries we have built to make this possible.

mkernel

mkernel is a small library that provides the essentials for developing a unikernel targeting Solo5 or Unikraft. Its purpose is to expose hypercalls (the unikernel equivalent of syscalls) so that your application can interact with network and block devices.

We recommend reading the mkernel documentation to understand the key concepts behind unikernels (hypercalls, devices, tenders, etc.). The slides we presented at the last MirageOS retreat are also helpful for understanding our new workflow. Compiling a unikernel now only requires dune and vendored libraries. We have therefore chosen not to resolve dependencies automatically (as opam-monorepo did) or to impose them (as the mirage tool does), giving developers full freedom to build their unikernels as they see fit. In particular, this makes it straightforward to use ppx rewriters, which was difficult or impossible with the mirage tool.

A new workflow and targets

This release is experimental: Xen is not yet supported, and Unikraft support is partial. However, both platforms are on our roadmap, so if you are interested in them, please let us know! We are also developing tooling to streamline the workflow, with the guiding principle that these tools should never dictate which dependencies your unikernel uses or which build system you choose.

mnet

mnet is the centerpiece of this release. It is designed to replace (and improve upon) mirage-tcpip by offering a direct-style API built on effects. The API deliberately mirrors the Unix socket interface: connect, listen, accept, read, write, and close all behave the way you would expect.

Here is a small example of an echo server:

let run (ipv4, gateway, ipv6) =
  Mkernel.(run [ rng; Mnet.stack ~name:"service" ?gateway ~ipv6 ipv4 ])
  @@ fun rng (daemon, tcp, udp) ->
  let@ () = fun () -> Mnet.kill daemon in
  let@ () = fun () -> Mirage_crypto_rng_mkernel.kill rng in
  let listen = Mnet.TCP.listen tcp 9000 in
  let flow = Mnet.TCP.accept tcp listen in
  let buf = Bytes.create 4096 in
  let rec echo () =
    let len = Mnet.TCP.read flow buf in
    if len > 0 then begin
      Mnet.TCP.write flow (Bytes.sub_string buf 0 len);
      echo ()
    end
  in
  let@ () = fun () -> Mnet.TCP.close flow in
  echo ()

TLS support (via ocaml-tls) is already available, allowing you to establish secure connections with your unikernel. DNS resolution (via ocaml-dns) and the Happy Eyeballs algorithm are also included, so that a unikernel can resolve hostnames and connect to remote services over both IPv4 and IPv6.

A short tutorial walks you through creating an echo server as a unikernel, similar to what we already offer for Miou. For the curious, an article about IPv4 and Miou is available here.

Beyond the move to effects for all scheduling, I would like to give special thanks to:

  • Edwin Török for proposing fixes to prevent denial-of-service attacks, which
    have been integrated into mnet (in particular for ARP).
  • Nicolas Ojeda Bär for contributing an IPv6 implementation that we adopted and
    improved (caches, packet fragmentation and reassembly, etc.)
  • Reynir for patiently debugging PCAP traces
  • Hannes for sharing his deep knowledge of the TCP protocol and its
    interactions with IPv4 and IPv6

As well as others who participated in the development of mnet in one way or another.

Developing and deploying unikernels

This library also marks a turning point in our approach to unikernels. We no longer treat a regular OCaml application as something that can be transparently turned into a unikernel. Developing a unikernel now means developing a unikernel from the start. In our experience, trying to retrofit an existing application into a unikernel was neither practical nor worthwhile.

Our tutorial therefore covers both the development and deployment of unikernels. On the deployment side, we also recommend exploring Albatross.

utcp

utcp is a pure OCaml implementation of the TCP protocol, used internally by mnet to handle TCP connections. The implementation is based on a state machine and performs no I/O itself, making it easy to test and reason about.

utcp covers the full TCP lifecycle: the three-way handshake, reliable in-order data delivery with retransmissions, flow control, congestion control, and connection teardown. It has been tested extensively through our end-to-end unikernel tests (a simple echo server and client, and a DNS resolver).

Unikernels and proofs

utcp also reflects our ambition to incorporate, as time permits, proof-based implementations. Other examples include:

  • miou with its priority queue (see the VOCAL project)
  • mirage-crypto with certain cryptographic primitives (see the fiat project)
  • and now utcp (see netsem)

This effort is still in its early stages, but we welcome improvements and collaborations in this area.

mhttp

To reach a wider audience, we also provide an HTTP protocol implementation for unikernels, building on the work we started (and currently use in production) with httpcats.

mhttp is essentially a unikernel-oriented counterpart to httpcats, using ocaml-h1 and ocaml-h2 as its HTTP/1.1 and H2 backends.

Ecosystem and composability

Although mhttp is a small project, it illustrates a broader principle behind our cooperative’s approach to library development. As we have stated before, everything we build is designed to be reusable with schedulers other than Miou and in contexts other than unikernels. Regardless of which scheduler one prefers, we believe in a pragmatic, cooperative approach to contributing to the community.

Our primary focus remains unikernel development, but years of experience building protocol and format libraries have taught us that keeping them scheduler-agnostic is in everyone’s best interest, for maintainability, testability, and reliability alike. Wherever possible, you will find a general-purpose library alongside its unikernel counterpart: httpcats and mhttp are one such pair.

Vifu

As previously announced, we have developed a web framework for OCaml 5 called vif, which we use in production for our builds.robur.coop website (we recommend the tutorial presented at FUN OCaml 2025).

vif now has a unikernel variant: vifu. It offers nearly the same interface as vif (except for static file management), making it possible to build websites entirely in OCaml. Here is a small unikernel that displays “Hello World!”:

let hello_world req _server () =
  let open Vifu.Response.Syntax in
  let* () = Vifu.Response.with_text req "Hello World!\n" in
  Vifu.Response.respond `OK

let run (ipv4, gateway, ipv6) =
  Mkernel.(run [ rng; Mnet.stack ~name:"service" ?gateway ~ipv6 ipv4 ])
  @@ fun rng (daemon, tcp, udp) ->
  let@ () = fun () -> Mnet.kill daemon in
  let@ () = fun () -> Mirage_crypto_rng_mkernel.kill rng in
  let cfg = Vifu.Config.v 80 in
  let routes = Vifu.[ get (rel /?? any) --> hello_world ] in
  Vifu.run ~cfg tcp routes ()

disclaimer: If you would like to use vifu, please note that although a recent release has been made, one final fix is still needed to compile a unikernel with this library (to avoid pulling in the unix module). We recommend pinning vifu for now.

Unikernels

Behind all these libraries, there are concrete projects that we would like to share with you:

  • Our immutable unikernel, a web server for static files (which can be generated, for example, with YOCaml).
  • A DNS resolver unikernel: pagejaune
  • A small search engine for emails from an archive such as the caml-list: blame. More details are available here.

Conclusion

After several years of work, we are now able to develop unikernels with OCaml 5 in earnest. We hope that our new workflow will be of interest to the community and that these libraries will be a useful contribution to the OCaml ecosystem.

If you would like to try it out, we recommend starting with the mnet tutorial and the mkernel documentation. Feel free to open issues or reach out to us if you have any questions!

16 Likes