Mailing list for all users of the OCaml language and system.
 help / color / mirror / Atom feed
* [Caml-list] uniker.nl - a new way to develop and deploy unikernels in OCaml
@ 2026-10-20 15:38 Romain Calascibetta
  0 siblings, 0 replies; only message in thread
From: Romain Calascibetta @ 2026-10-20 15:38 UTC (permalink / raw)
  To: OCaml Mailing List

[-- Attachment #1: Type: text/plain, Size: 21020 bytes --]

This is a cross-post in Markdown format that you can find here:
https://discuss.ocaml.org/t/ann-uniker-nl-unikernels-in-ocaml/18625

We are delighted to announce the launch of our new website:
[uniker.nl][uniker.nl]. This website embodies our new approach to developing
unikernels with OCaml 5. It offers a new way of developing unikernels and
now
places just as much emphasis on the deployment and use of unikernels. The
website features a comprehensive [tutorial][uniker.nl-tutorial] and
resources
to help you get to grips with our new ecosystem, providing a _first entry
point_ for anyone interested in unikernels in OCaml.

The content of this site is available [here][uniker.nl-repo], is powered by
the
excellent [YOCaml][yocaml] project, and is deployed using our
[immuable][immuable] unikernel.

## Our new ecosystem

We were able to present our new workflow at the [OCaml Workshop
2026][workshop]
([slides][slides]) and our approach can be summarised in two main points:
- the development of unikernels
- the deployment of unikernels and their uses

### To develop unikernels

Our co-operative has been developing unikernels for quite a few years now.
[Our
blog][blog-robur] showcases some of the ones we use for our infrastructure.
Based on our experience, we are well aware of the constraints and
limitations
of what we can achieve when it comes to developing an application in the
form
of a unikernel. The best-known constraint we have accepted is not to require
the POSIX interface (nor to attempt to replicate it) in terms of protocols
and
formats. One of the rules still adhered to today is, amongst other things,
[not
to depend on I/O or a scheduler][sans-io]<sup>[1][sans-io-2]</sup> when
providing implementations.

Building on this experience, we have been able to develop protocols and
formats
that are currently being used by others in contexts other than unikernels.

However, we have also seen the limits of what we can achieve, particularly
with
what MirageOS currently offers. With this in mind, and upon further
reflection
on the constraints we face when developing unikernels (particularly
regarding
cross-compilation), we have decided to structure the development of
unikernels
using two tools: [`unic`][unic] and [`mfetch`][mfetch].

#### [`unic`][unic]: how to infer dependencies and _vendor_ them

One constraint we face with unikernels is the need to recompile C files with
specific options so that they do not depend on what already exists on the
host
system. The approach taken by MirageOS 3 was to distribute the result of
this
specific compilation within libraries that contain C stubs (such as
[`digestif`][digestif] or [`mirage-crypto`][mirage-crypto]). The problem is
that we were forcing users of these libraries to install
[`ocaml-solo5`][ocaml-solo5] in order to obtain a compilation environment
for C
files specific to [Solo5][solo5], our core component for producing
unikernels.

MirageOS 4 then arrived with `dune` and its contexts, allowing us to
achieve a
consistent compilation environment for C files. However, this approach
introduced new a constraint: all dependencies of our unikernels had to use
`dune`, which is **legitimately** not the case in the OCaml ecosystem.

It is for this reason that we decided to refine the need to recompile some
of
our libraries a little further and, finally, to be able to capitalise on a
key
advantage of [`ocaml-solo5`][ocaml-solo5], namely that compiling OCaml files
with this compiler produces exactly the same result as if you were using
your
host OCaml compiler: in other words, and as has been the case from the
beginning, we strictly only need to cross-compile the C files.

It is therefore at the intersection of these two features (only needing to
cross-compile the C files and being able to reuse libraries written in pure
OCaml and compiled with the host compiler) that we can offer users the
ability
to write this sort of code without involving any maintenance work on
libraries
that do not use `dune` and without necessarily having to recompile
everything:

```shell
$ opam install unic
$ cat >main.ml<<EOF
> open Digestif

> let () = Mkernel.run [] @@ fun () ->
>   Fmt.pr "%a\n%!" SHA1.pp (SHA1.digest_string "Hello World!")
> EOF
$ unic infer -r .
Module Digestif is provided by several ocamlfind packages:
  [0] digestif.c
  [1] digestif.ocaml
Pick one [0-1]: 0
digestif
mkernel
$ opam source digestif
$ opam source mkernel
$ dune build
$ solo5-hvt -- _build/solo5/main.exe --solo5:quiet
2ef7bde608ce5404e97d5f042f95f89f1c232871Solo5
```

`unic` is therefore a tool capable of inferring the dependencies of an OCaml
project without using a specific build system, and of calculating what
needs to
be cross-compiled (in this case `digestif` and `mkernel` but not `fmt`) to
produce a unikernel. The project serves our purpose first and foremost, but
it
is worth noting that it can also be useful when compiling statically linked
executables.

This tool offers a whole range of sub-tools for analysing META files,
viewing
an artefact from different perspectives (its module name, its `ocamlfind`
package name and its OPAM package name) or analysing OCaml artefacts (such
as
`*.cmxa` files).

We would particularly like to thank @Octachron for his [`codept`][codept]
project (on which `unic` is based) and for working with us to improve it.

### [`mfetch`][mfetch]: how to vendor dependencies

Having identified more precisely which dependencies need to be
cross-compiled,
we now need to download these dependencies. Although `opam source` does the
job
very well, we wanted a tool that could download the source code from various
types of source, such as:
- from a _tarball_, as is usually the case
- but also from a Git repository
- or even from a specific location on your file system

That is why we developed `mfetch`, which allows you to download software
sources based on information provided by OPAM or the user, and to create a
`vendors` folder containing all these sources.

```shell
$ unic infer -r . -o _mfetch
$ mfetch --with-dune-file
 digestif                 │██████████████████│    3.1 MiB /    3.1 MiB
 5.9 MiB/s
 mkernel                  │██████████████████│   37.3 KiB /   37.3 KiB
 3.4 MiB/s

digestif                         ok
mkernel                          ok
$ dune build
```

`mfetch` also allows you to check whether you have modified any of the
source
files (for a bug fix) or to generate a `*.locked` file so that you can then
verify the reproducibility of your unikernel using our other tool
[`orb`][orb].
The tool itself is not revolutionary, but it helps to streamline the
workflow.

```shell
$ mfetch status
digestif                         ok
mkernel                          ok
$ mfetch lock
2 sources locked into _mfetch.locked
```

#### Libraries for OCaml 5

Our new workflow now uses OCaml 5. As well as updating the basic building
blocks required to produce unikernels with OCaml 5, we have also developed a
series of libraries that primarily enable us to take advantage of effects.
Furthermore, over the past several years we have developed:
- [Miou][miou-announce], our scheduler for OCaml 5
- [`mkernel`][mkernel], a derivative of our Miou scheduler designed to
handle
  the I/O provided by Solo5
- [`mnet`][mnet-announce], a reimplementation of the TCP/IP stack (using
  [`utcp`][utcp]) in OCaml with effects
- and plenty of other libraries such as [`mhttp`][mhttp],
  [`msendmail`][msendmail], [`mfat`][mfat], [`vifu`][vifu], etc.

These libraries, currently used by some of our deployed unikernels, allow
us to
offer developers a _direct-style_ approach to implementing their unikernels.

But what is key to take away from this emerging ecosystem is the
retrospective
analysis of past projects to ensure we do not repeat the same mistakes. We
very
quickly realised that it could be very difficult to migrate existing
libraries
using [`lwt`][lwt] (the MirageOS scheduler) to Miou (or any other scheduler
using effects). We therefore took the opportunity not only to propose a
_direct-style_ approach using Miou, but also to rethink the implementation
of
our libraries in light of what we've learnt from deploying unikernels with
MirageOS. We have paid particular attention to memory usage, to avoiding
known
CVEs from our previous implementations (as we are also part of the OCaml
security team), and also to offering an alternative API that does not
systematically rely on functors.

The approach is still experimental, as we need to consolidate this new
ecosystem across several layers, but we are firmly convinced (based on what
we've observed in our deployed unikernels) that these new implementations
are
significantly better than what we used in the past.

### To deploy unikernels

The other side of unikernels is deployment. Indeed, it is not just a matter
of
developing unikernels (and making their development easier) but also of
deploying and using them. Our main objective remains to replace existing
services with unikernels (name servers, websites, time servers, etc.), and
the
way to compete with existing infrastructure is to actively deploy and use
our
unikernels.

That is why we have developed two projects:
- [Albatross][albatross], an OCaml service for launching unikernels
- [Aussi][aussi], an OCI runtime for Solo5

We mainly use Albatross in conjunction with our infrastructure for verifying
the reproducibility of our unikernels: [builds.robur.coop][builds.robur.coop
].
In this way, we can trace the dependencies of the unikernels - particularly
their CVEs - and ensure supply-chain security.

Next comes deployment, and we are therefore seeking to integrate our
unikernels
into existing infrastructure and provide ways of monitoring them, in
particular
using [InfluxDB][influxdb], [Telegraf][telegraf] and [Grafana][grafana].

This is how we are able to offer services such as (note that some haven't
yet
been ported to `mkernel`/`mnet`, but will be soon):
- simple websites such as our https://blog.robur.coop/ (using
[`unipi`][unipi]
  or [`immuable`][immuable])
- domain name management for domains such as `robur.coop`, `reyn.ir` or
  `din.osau.re` (via our [primary][dns-primary-git] DNS server and
  [secondary][mnet-secondary] server)
- a VPN service, whether using [MirageVPN][MirageVPN] or [Zostera][zostera]
- a time server (using [Chaos][chaos]): https://chaos.osau.re/
- a [CalDAV][caldav] calendar system
- [a mailing list][mailingl.st] using the [`ptt`][ptt] project
- a TLS termination proxy such as [`tlstunnel`][tlstunnel] or
  [`contruno`][contruno] (used by sherlocode.com)
- and many more!

It is therefore through actual use that we improve our unikernels and the
libraries that depend on them. That is why part of our tutorial also
focuses on
deploying unikernels, so that you can use them and help us with your
feedback.

Finally, we are also proposing a project called [Mollymawk][mollymawk],
designed to simplify the deployment of unikernels via a web interface, so
that,
in future, we can offer access to our infrastructure to anyone who wishes to
use it (and we would like to thank @kit-ty-kate for their various attempts).

## About us

Finally, none of this would be possible without our co-operative and the
way we
organise our work. You can view the details of our finances
[here][robur-finance]. We accept donations and have recently secured funding
from NLnet. Private companies such as [Ahrefs][ahrefs] and
[Tarides][tarides]
also sponsor our work. We also have partnerships with [OCamlPro][ocamlpro]
and
the [OCaml Foundation][ocf]. And we contribute to the OCaml ecosystem on a
daily basis by working with individuals and companies.

You can find more details on [our website][robur.coop]. We would like to
emphasise that it is thanks to our structure (that of an association) that
we
are able to carry out this work, and we would like to thank our sponsors.

## Unikernels and MirageOS

Enthusiasts may wonder what the advantage of our approach is compared to
MirageOS, a unikernel project that remains active thanks to our maintenance
work. Our approach is characterised by five points that we have identified
as
glass ceilings to the adoption of unikernels:
- the first concerns the use of [`ocaml-cstruct`][ocaml-cstruct]. The
  fundamental issue remains the use of `bigarray` as _buffer_ instead of
  `bytes`/`string`. Our experience whilst developing [MirageVPN][miragevpn]
has
  shown us that using `cstruct` as a systematic library for the _buffer_
  concept Across our entire ecosystem was our biggest **penalty** in terms
of
  throughput for our unikernel. This is why we decided not only to develop
  [`bstr`][bstr] and [`slice`][bstr], but also to rewrite the majority of
our
  libraries by replacing `cstruct` with `bytes`/`string` (this was the case
for
  [`ocaml-tls`][ocaml-tls-pr], [`mirage-crypto`][mirage-crypto-pr],
  [`ocaml-dns`][ocaml-dns-pr] and, more recently, [`awa-ssh`][awa-ssh-pr]
and
  [`utcp`][utcp-pr]). The conclusion is quite simple: it is not a question
of
  stopping using bigarray, but rather of not making their use systematic in
our
  aPIs.
- the second point concerns the use of functors to express the devices
required
  by the unikernel. The approach of reusing interfaces to inject specific
  implementations that concretize these devices via functors may seem
  appealing, but the maintenance cost of such an ecosystem—constrained by
  interfaces that are no longer really up to date with new concepts—has led
us
  into [absurd situations][absurd] whenever we've needed to develop more
  specific unikernels tailored to our requirements. Once again, the rule is
not
  to stop using functors, but not to make their use systematic.
- the third point concerns the compilation of unikernels. MirageOS relies
on a
  workaround that, even today, integrates very poorly with `dune`; this can
be
  defined as: _multi-stage compilation_, where a code-generation phase is
  required depending on the desired _target_. This way of structuring the
  workflow with a set of dependencies dictated by the tool explicitly
restricts
  the developer in what they wish to do and how they wish to do it. Although
  there are constraints on compiling a unikernel (such as not using the
`Unix`
  module), our cooperative has decided to provide **informative** (and not
  prescriptive) tools that assist (but do not force) the user in designing
  their unikernel (furthermore, we are also delighted to introduce
  [`unic`][unic] and [`mfetch`][mfetch]). The key point of our new approach
is
  to ensure that it is the user who has the final say, rather than the tool
(in
  this case, `mirage`), in the development of unikernels.

  It is very intricate on what the `mirage` tool does, including generation
of
  functor applications and some initialization stubs, as well as opam
version
  constraints (since the functors and initialization functions may change in
  new opam packages). The `mirage` tool has a closed world assumption, and
it
  is hard to use a package not known by `mirage` - it is as well very hard
to
  use `js_of_ocaml` in a `mirage` unikernel.
- the fourth point concerns the maintenance of this ecosystem, and
specifically
  the maintenance of the tools and libraries on which MirageOS relies.
  Furthermore, we have experienced some disappointments with
`opam-monorepo`,
  and we are finding, day by day, that it is becoming increasingly
difficult to
  update `mirage-tcpip` in line with OCaml 5. Our involvement in the OCaml
  security team has also led us to wish to deprecate certain libraries due
to a
  lack of maintenance. We are therefore refocusing our ecosystem on what our
  co-operative produces and maintains.
- Finally, the last point concerns our work with the community, which
remains
  unchanged despite the emergence of AI. We have observed a certain abusive
use
  of AI to solve non-existent problems relating to unikernels, which, in our
  view, undermines our credibility regarding the security, maintenance and
  quality of our software. In this regard, we are trying to distance
ourselves
  from those who prefer _vibe-coding_ rather than deployment. This approach
is
  reflected in our use of the [AGPL][agpl] as our licence (whilst offering
the
  option of dual licensing) and a [POLICY][policy] regarding the use of AI.

All of this has led us to propose MirageOS 5, which represents a fundamental
departure from the factors that characterised the failure of MirageOS 4 (for
which I was the release manager). It is therefore, having acknowledged this
defeat (and analysed it), that we are proposing a new approach to creating
and
deploying unikernels: [uniker.nl][uniker.nl].

As regards the projects associated with the MirageOS project, we will, of
course, continue to maintain some of them, whilst at the same time offering
more up-to-date solutions to those who use some of our libraries. We would
also
be delighted to help anyone wishing to migrate to our new libraries and
tools;
we are (as we always have been) available to assist.

[unic]: https://github.com/robur-coop/unic
[mfetch]: https://github.com/robur-coop/mfetch
[agpl]: https://www.gnu.org/licenses/agpl-3.0.en.html
[policy]: https://github.com/Solo5/solo5/blob/main/AI.md
[ocaml-cstruct]: https://github.com/mirage/ocaml-cstruct
[miragevpn]: https://github.com/robur-coop/miragevpn
[tarides]: https://tarides.com/
[ahrefs]: https://ahrefs.com/
[robur-finance]: https://blog.robur.coop/articles/finances.html
[mollymawk]: https://mollymawk.robur.coop/
[bstr]: https://github.com/robur-coop/bstr
[immuable]: https://github.com/dinosaure/immuable
[uniker.nl]: https://uniker.nl/
[builds.robur.coop]: https://builds.robur.coop
[zostera]: https://github.com/robur-coop/zostera
[workshop]: https://ocaml.org/ocaml-workshop-2026
[slides]:
https://docs.google.com/presentation/d/1bOXJAPEEjPBx_8i0frThKhLpHd8C7-EPRczEQjvz6JU/edit?slide=id.g3f7268904c5_0_1#slide=id.g3f7268904c5_0_1
[uniker.nl-tutorial]: https://uniker.nl/introduction/
[yocaml]: https://yocaml.github.io/tutorial/index.html
[blog-robur]: https://blog.robur.coop/
[miou-announce]:
https://discuss.ocaml.org/t/ann-miou-a-simple-scheduler-for-ocaml-5/12963
[sans-io]: https://discuss.ocaml.org/t/on-concurrency-models/15899/13
[sans-io-2]:
https://discuss.ocaml.org/t/why-ocsigen-is-staying-on-lwt-for-now/18318/27
[digestif]: https://github.com/mirage/digestif
[mnet]:
https://discuss.ocaml.org/t/ann-mnet-a-new-tcp-ip-stack-for-unikernels-in-ocaml/17851
[mirage-crypto]: https://github.com/mirage/mirage-crypto
[solo5]: https://github.com/solo5/solo5
[mkernel]: https://github.com/robur-coop/mkernel
[lwt]: https://github.com/ocsigen/lwt
[tlstunnel]: https://github.com/robur-coop/tlstunnel
[contruno]: https://github.com/robur-coop/contruno
[mailingl.st]: https://mailingl.st
[ptt]: https://github.com/robur-coop/ptt
[albatross]: https://github.com/robur-coop/albatross
[aussi]: https://github.com/robur-coop/aussi
[chaos]: https://github.com/robur-coop/chaos
[unipi]: https://github.com/robur-coop/unipi
[mnet]: https://github.com/robur-coop/mnet
[ocaml-solo5]: https://github.com/mirage/ocaml-solo5
[utcp]: https://github.com/robur-coop/utcp
[codept]: https://github.com/Octachron/codept
[mnet-announce]:
https://discuss.ocaml.org/t/ann-mnet-a-new-tcp-ip-stack-for-unikernels-in-ocaml/17851
[mhttp]: https://github.com/robur-coop/mhttp
[mfat]: https://github.com/robur-coop/mfat
[vifu]: https://github.com/robur-coop/vif
[msendmail]: https://github.com/robur-coop/colombe
[dns-primary-git]: https://github.com/robur-coop/dns-primary-git/
[mnet-secondary]: https://git.robur.coop/robur/dns-secondary/
[uniker.nl-repo]: https://github.com/robur-coop/uniker.nl
[orb]: https://github.com/robur-coop/orb
[grafana]: https://grafana.com/
[influxdb]: https://www.influxdata.com/
[telegraf]: https://www.influxdata.com/time-series-platform/telegraf/
[ocamlpro]: https://ocamlpro.com
[ocf]: https://ocaml-sf.org/
[robur.coop]: https://robur.coop/
[ocaml-tls-pr]: https://github.com/mirleft/ocaml-tls/pull/497
[mirage-crypto-pr]: https://github.com/mirage/mirage-crypto/pull/214
[ocaml-dns-pr]: https://github.com/mirage/ocaml-dns/pull/351
[awa-ssh-pr]: https://github.com/mirage/awa-ssh/pull/85
[utcp-pr]: https://github.com/robur-coop/utcp/pull/55
[absurd]:
https://github.com/robur-coop/dnsvizor/blob/83b32596c4a08c080067e1cd36fe5788b90cdd78/unikernel.ml#L610-L631
[caldav]: https://github.com/robur-coop/caldav

-- 
Romain Calascibetta - http://din.osau.re/

[-- Attachment #2: Type: text/html, Size: 26404 bytes --]

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-20 15:38 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-20 15:38 [Caml-list] uniker.nl - a new way to develop and deploy unikernels in OCaml Romain Calascibetta

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox