[ANN] Mirage 3.2.0




it is my pleasure to announce that mirage 3.2.0 has been released to opam-repository(*). This release contains breaking changes with earlier releases:

It requires solo5 0.4.0, which renames “ukvm” to “hvt” (thanks to Martin Lucina)
It requires tcpip 3.5.0, which removes complexity from stack initialisation (thanks to Hannes Mehnert)

Mirage now comes with the hvt (Hardware Virtualization Tender) target (earlier known as ukvm), which works on Linux/KVM, OpenBSD/VMM (since 6.4), and FreeBSD/VMM|BHyve.

Upgrading from Mirage 3.1.x or earlier

Due to conflicting packages, opam will not upgrade mirage to version 3.2.0 or newer if a version of mirage-solo5 older than 0.4.0 is installed in the switch. To perform the upgrade you must run opam upgrade mirage explicitly.

Changes required to rebuild and run ukvm unikernels

As of Solo5 0.4.0, the ukvm target has been renamed to hvt. If you are working out of an existing, dirty, source tree, you should initially run:

mirage configure -t hvt
mirage clean
mirage configure -t hvt

and then proceed as normal. If you are working with a clean source tree, then simply configuring with the new hvt target is sufficient:

mirage configure -t hvt

Note that the build products have changed:

The unikernel binary is now named <unikernel>.hvt, the ukvm-bin binary is now named solo5-hvt.

adapt to mirage-protocols, mirage-stack, tcpip changes

This is a breaking change: mirage 3.2.0 requires mirage-protocols 1.4.0, mirage-stack 1.3.0, and tcpip 3.5.0 to work (charru-client-mirage 0.10 and mirage-qubes-ipv4 0.6 are adapted to the changes). An older mirage won’t be able to use these new libraries correctly. Conflicts were introduced in the opam-repository.

In more detail, direct and socket stack initialisation changed, which is automatically generated by the mirage tool for each unikernel (as part of main.ml). A record was built up, which is no longer needed.

Several unneeded type aliases were removed:
netif from Mirage_protocols.ETHIF
ethif and prefix from Mirage_protocols.IP
ip from Mirage_protocols.{UDP,TCP}
netif and 'netif config from Mirage_stack.V4
'netif stackv4_config and socket_stack_config in Mirage_stack



*: which since a week switched to opam 2.0, and thus you’ll only see updates if you upgraded your opam to 2.0 (you can do so by following the instructions on https://opam.ocaml.org/blog/opam-2-0-0/)


Could you give a summary (or provide a link) what Mirage is and what it is good for?


You can see Mirage as the sum of three components:

  1. A set of system APIs. We have module types for each of the main device drivers (low-level like network and storage but also higher level drivers like HTTP and TLS devices). See for instance mirage-protocols for the module types related to our TCP/IP stack.

  2. A set of packages implementing these API. These could be backend-specific (like Unix, Xen, or solo5 (to target kvm), or ESP32) or generic (like an HTTP server, a Git implementation, etc). The generic implementations are functors over the set of APIs defined in 1.

  3. A configuration DSL (functoria) and a command-line tool (mirage) to bind them all.

You can read more about this on the MirageOS website, including links to papers, tutorials, etc.


And here a few examples of how to use MirageOS:

  • you want to have a total control on your runtime environment: you can use mirage to build a very specialised OS that you can statically link to your application. This removes the need an underlying general purpose OS and produce small deployment images (a few Mo) with low ressource consumptions (a few Mo), so you could increase the density of your deployments. The images are also secure as the attack surfaces is reduced, see for instance the bitcoin pinata.

  • you want to build a “system” application (e.g. a firewall, a reverse TCP/IP stack): you can pick the Mirage libraries that you need an include these in your application. See for instance VPNKit or Qubes firewall.


Perhaps as an outsider I can give a somewhat different summary.

Mirage is a library that acts as an operating system — it allows you to construct an OCaml program that boots directly on top of a virtual machine hypervisor by providing your program all the system services it needs to boot on not-quite-bare metal. It has all the services you would usually expect in an operating system, like a TCP/IP stack and drivers and all the rest.

(The Unikernel wikipedia page gives some more explanation of the approach.)

This lets you construct very small, very specialized machine images where the entire stack is written in OCaml. If you want to build (say) a very small, very secure system appliance (like a small firewall + router), this is one cool way to do it. That said, you can in theory build any sort of “boots on bare metal” system you like with it, just as you can put any application you like on top of Unix. The limitation is that everything runs in one address space, but that’s also the joy of it.


BTW, I have a couple of questions I’ve never bothered trying to get the answer for until now:

  1. How does Mirage handle multiple processors?
  2. Can Mirage boot on actual bare metal?


It doesn’t; like most unikernels I’ve seen to date the design choice is to remain single-core and use a cooperative scheduler. If you want to scale out to multiple cores then the idea is you run multiple instances of your unikernel, and the scheduling of unikernels to cores is handled by the hypervisor (or other “host” system).

It can, but the only example I know of to date is the ESP32 port (blog, implementation).

The OCaml runtime is extremely easy to re-target to anything and requires only minimal C dependencies (see e.g. ocaml-freestanding), so actual bare metal is mainly a question of writing the low-level platform startup code, and then the actual hardware drivers you might need (which can be in OCaml).

By the way, thank you for your summary write-up of Mirage. Sometimes it’s hard to explain to people as an insider what exactly a project is good for.


Ah, that’s a little unfortunate for something like building a standalone router. You might want to do ethernet i/o and packet switching on many cores for performance. Will the OCaml multicore branch help with this when it gets integrated?


Oh, and cool how easy it is to make it run on bare metal. I wonder if I should try that with an embedded processor at some point. Does the Mirage repository have USB controller and similar drivers?


No, there isn’t any USB (or other) drivers yet.

Just to clear up the wording here: there is no “mirage repository”, but being a library operating system, each MirageOS unikernel consists of hundreds of OCaml libraries, all released to the opam-repository.


This is surely one of my favorite projects. Thanks!


The bare metal target is exciting. Mirage might also turn out to be a nice environment to try out new OS designs / features. Might be ideal for a lot of embedded service apps, especially dapps where you want to minimize attack surfaces.