v4.10.4 (2025-11-21)

v4.10.3 (2025-10-27)

v4.10.2 (2025-10-03)

v4.10.1 (2025-09-09)

v4.10.0 (2025-09-08)

v4.9.0 (2025-03-04)

  --- a/tutorial/hello-key/config.ml
  +++ b/tutorial/hello-key/config.ml
  @@ -2,5 +2,5 @@
   open Mirage
   
   let packages = [ package "duration" ]
  -let main = main ~packages "Unikernel.Hello" (time @-> job)
  -let () = register "hello-key" [ main $ default_time ]
  +let main = main ~packages "Unikernel" job
  +let () = register "hello-key" [ main ]

  --- a/tutorial/hello-key/unikernel.ml
  +++ b/tutorial/hello-key/unikernel.ml
  @@ -5,13 +5,12 @@ let hello =
     let doc = Arg.info ~doc:"How to say hello." [ "hello" ] in
     Mirage_runtime.register_arg Arg.(value & opt string "Hello World!" doc)
   
  -module Hello (Time : Mirage_time.S) = struct
  -  let start _time =
  +let start () =
     let rec loop = function
       | 0 -> Lwt.return_unit
       | n ->
         Logs.info (fun f -> f "%s" (hello ()));
  -          Time.sleep_ns (Duration.of_sec 1) >>= fun () -> loop (n - 1)
  +      Mirage_sleep.ns (Duration.of_sec 1) >>= fun () ->
  +      loop (n - 1)
     in
     loop 4
  -end

v4.8.2 (2024-12-09)

v4.8.1 (2024-10-14)

v4.8.0 (2024-10-08)

v4.7.0 (2024-09-05)

v4.6.1 (2024-08-08)

v4.6.0 (2024-06-26)

v4.5.1 (2024-05-17)

v4.5.0 (2024-04-09)

v4.4.2 (2024-02-13)

v4.4.1 (2023-11-20)

v4.4.0 (2023-06-19)

v4.3.6 (2023-03-28)

v4.3.5 (2023-03-20)

Changed

v4.3.4 (2023-02-23)

Fixed
Added
Changed

v4.3.3 (2023-01-30)

Fixed
Changed

v4.3.2 (2022-12-12)

Fixed
Changed
Added

v4.3.1 (2022-10-25)

Fixed
Changed
Added

v4.3.0 (2022-09-26)

Fixed
Changed

v4.2.1 (2022-08-25)

Fixed

v4.2.0 (2022-07-26)

Fixed
Changed

v4.1.1 (2022-04-05)

Fixed

v4.1.0 (2022-05-02)

Changed
Added

v4.0.0 (2022-03-28)

Fixed
Changed
Added

v4.0.0~beta3 (2022-02-02)

v4.0.0~beta2 (2022-01-31)

v4.0.0~beta1 (2022-01-29)

Refactor build process to use Dune build system. The motivation is to drop ocamlbuild-induced technical debt and to obtain first-class support for cross-compilation. To learn more about how Dune is able to perform cross-compilation, please refer to the documentation.

Main changes:

Breaking changes:

v3.10.8 (2021-12-17)

v3.10.7 (2021-12-09)

v3.10.6 (2021-10-20)

v3.10.5 (2021-10-09)

v3.10.4 (2021-04-20)

v3.10.3 (2021-04-19)

v3.10.2 (2021-03-30)

v3.10.1 (2020-12-04)

v3.10.0 (2020-12-02)

IPv6 and dual (IPv4 and IPv6) stack support #1187

Since a long time, IPv6 code was around in our TCP/IP stack (thanks to @nojb who developed it in 2014). Some months ago, @hannesm and @MagnusS got excited to use it. After we managed to fix some bugs and add some test cases, and writing more code to setup IPv6-only and dual stacks, we are eager to share this support for MirageOS in a released version. We expect there to be bugs lingering around, but duplicate address detection (neighbour solicitation and advertisements) has been implemented, and (unless "--accept-router-advertisement=false") router advertisements are decoded and used to configure the IPv6 part of the stack. Configuring a static IPv6 address is also possible (with "--ipv6=2001::42/64").

While at it, we unified the boot arguments between the different targets: namely, on Unix (when using the socket stack), you can now pass "--ipv4=127.0.0.1/24" to the same effect as the direct stack: only listen on 127.0.0.1 (the subnet mask is ignored for the Unix socket stack).

A dual stack unikernel has "--ipv4-only=BOOL" and "--ipv6-only=BOOL" parameters, so a unikernel binary could support both Internet Protocol versions, while the operator can decide which protocol version to use.

Please also note that the default IPv4 network configuration no longer uses 10.0.0.1 as default gateway (since there was no way to unset the default gateway #1147).

For unikernel developers, there are some API changes in the Mirage module

Some parts of the Mirage_key module were unified as well:

If you're ready to experiment with the dual stack, here's a diff for our basic network example (from mirage-skeleton/device-usage/network) replacing IPv4 with a dual stack:

diff --git a/device-usage/network/config.ml b/device-usage/network/config.ml
index c425edb..eabc9d6 100644
--- a/device-usage/network/config.ml
+++ b/device-usage/network/config.ml
@@ -4,9 +4,9 @@ let port =
   let doc = Key.Arg.info ~doc:"The TCP port on which to listen for incoming connections." ["port"] in
   Key.(create "port" Arg.(opt int 8080 doc))

-let main = foreign ~keys:[Key.abstract port] "Unikernel.Main" (stackv4 @-> job)
+let main = foreign ~keys:[Key.abstract port] "Unikernel.Main" (stackv4v6 @-> job)

-let stack = generic_stackv4 default_network
+let stack = generic_stackv4v6 default_network

 let () =
   register "network" [
diff --git a/device-usage/network/unikernel.ml b/device-usage/network/unikernel.ml
index 5d29111..1bf1228 100644
--- a/device-usage/network/unikernel.ml
+++ b/device-usage/network/unikernel.ml
@@ -1,19 +1,19 @@
 open Lwt.Infix

-module Main (S: Mirage_stack.V4) = struct
+module Main (S: Mirage_stack.V4V6) = struct

   let start s =
     let port = Key_gen.port () in
-    S.listen_tcpv4 s ~port (fun flow ->
-        let dst, dst_port = S.TCPV4.dst flow in
+    S.listen_tcp s ~port (fun flow ->
+        let dst, dst_port = S.TCP.dst flow in
         Logs.info (fun f -> f "new tcp connection from IP %s on port %d"
-                  (Ipaddr.V4.to_string dst) dst_port);
-        S.TCPV4.read flow >>= function
+                  (Ipaddr.to_string dst) dst_port);
+        S.TCP.read flow >>= function
         | Ok `Eof -> Logs.info (fun f -> f "Closing connection!"); Lwt.return_unit
-        | Error e -> Logs.warn (fun f -> f "Error reading data from established connection: %a" S.TCPV4.pp_error e); Lwt.return_unit
+        | Error e -> Logs.warn (fun f -> f "Error reading data from established connection: %a" S.TCP.pp_error e); Lwt.return_unit
         | Ok (`Data b) ->
           Logs.debug (fun f -> f "read: %d bytes:\n%s" (Cstruct.len b) (Cstruct.to_string b));
-          S.TCPV4.close flow
+          S.TCP.close flow
       );

     S.listen s

Other bug fixes include #1188 (in #1201) and adapt to charrua 1.3.0 and arp 2.3.0 changes (#1199).

v3.9.0 (2020-10-24)

The Xen backend is a minimal legacy-free re-write: Solo5 (since 0.6.6) provides the low-level glue code, and ocaml-freestanding provides the OCaml runtime. The PV-only Mini-OS implementation has been retired.

The only supported virtualization mode is now Xen PVH (version 2 or above), supported since Xen version 4.10 or later (and Qubes OS 4.0).

The support for the ARM32 architecture on Xen has been removed.

Security posture improvements:

With the move to a Solo5 and ocaml-freestanding base MirageOS gains several notable improvements to security posture for unikernels on Xen:

Interface changes:

Other changes:

Acknowledgements:

v3.8.1 (2020-09-22)

v3.8.0 (2020-06-22)

v3.7.7 (2020-05-18)

v3.7.6 (2020-03-18)

v3.7.5 (2020-03-15)

v3.7.4 (2019-12-20)

v3.7.3 (2019-12-17)

v3.7.2 (2019-11-18)

v3.7.1 (2019-11-03)

v3.7.0 (2019-11-01)

v3.6.0 (2019-10-02)

v3.5.2 (2019-08-22)

v3.5.1 (2019-07-11)

v3.5.0 (2019-03-03)

v3.4.1 (2019-02-05)

v3.4.0 (2019-01-11)

3.3.1 (2018-11-21)

3.3.0 (2018-11-18)

New target: (via solo5) Genode: "Genode is a free and open-source operating system framework consisting of a microkernel abstraction layer and a collection of userspace components. The framework is notable as one of the few open-source operating systems not derived from a proprietary OS, such as Unix. The characteristic design philosophy is that a small trusted computing base is of primary concern in a security oriented OS." (from wikipedia, more at https://genode.org/ #942, by @ehmry)

User-visible changes

3.2.0 (2018-09-23)

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.

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

3.1.1 (2018-08-01)

3.1.0 (2018-06-20)

3.0.8 (2017-12-19)

3.0.7 (2017-11-24)

3.0.6 (2017-11-16)

3.0.5 (2017-08-08)

Packaging updates for latest opam repository:

3.0.4 (2017-06-15)

3.0.2 (2017-03-15)

3.0.1 (2017-03-14)

3.0.0 (2017-02-23)

2.9.1 (2016-07-20)

2.9.0 (2016-04-29)

2.8.0 (2016-04-04)

2.7.3 (2016-03-20)

2.7.2 (2016-03-20)

2.7.1 (2016-03-17)

2.7.0 (2016-02-17)

The mirage tool is now based on functoria. (#441 #450, by @drup @samoht) See https://mirage.io/blog/introducing-functoria for full details.

The "nocrypto" library is loaded but entropy is not enabled! Please enable the entropy by adding a dependency to the nocrypto device. You can do so by adding ~deps:[abstract nocrypto] to the arguments of Mirage.foreign.

  Data dependencies (such as entropy initialization) are now explicit.
In order to fix this, you need to declare the dependency like so:
```ocaml
open Mirage

let my_functor =
let deps = [abstract nocrypto] in
foreign ~deps "My_Functor" (foo @-> bar)

My_functor.start will now take an extra argument for each dependencies. In the case of nocrypto, this is ().

2.6.1 (2015-09-08)

2.6.0 (2015-07-28)

2.5.1 (2015-07-17)

2.5.0 (2015-06-10)

(* [config.ml] ) ( in 2.4 ) let http = http_server (TCP (Port 80)) conduit ( in 2.5 *) let http = http_server conduit

(* [unikernel.ml] ) let start http = ( in 2.4 ) http (S.make ~conn_closed ~callback ()) ( in 2.5 *) http (`TCP 80) (S.make ~conn_closed ~callback ()) ```

(* [config.ml] *) let conduit = conduit_direct ~tls:true (stack default_console)

(* [unikernel.ml] *) module Main (C: Conduit_mirage.S): struct let start conduit = C.listen conduit (TLS (tls_config, TCP 443)) callback end ```

2.4.0 (2015-05-05)

2.3.0 (2015-03-10)

2.2.1 (2015-01-29)

2.2.0 (2014-12-18)

type v4 type v6

type 'a ip type ipv4 = v4 ip type ipv6 = v6 ip ```

Full support for configuring IPv6 does not exist yet, as this release is intended for getting the type definitions in place before adding configuration support.

2.1.1 (2014-12-10)

2.1.0 (2014-12-07)

2.0.1 (2014-11-21)

2.0.0 (2014-11-05)

1.2.0 (2014-07-05)

The Mirage frontend tool now generates a Makefile with a make depend target, instead of directly invoking OPAM as part of mirage configure. This greatly improves usability on slow platforms such as ARM, since the output of OPAM as it builds can be inspected more easily. Users will now need to run make depend to ensure they have the latest package set, before building their unikernel with make as normal.

1.1.3 (2014-06-15)

1.1.2 (2014-04-01)

1.1.1 (2014-02-21)

1.1.0 (2014-02-05)

1.0.4 (2014-01-14)

1.0.3 (2013-12-18)

1.0.2 (2013-12-10)

1.0.1 (2013-12-09)

1.0.0 (2013-12-09)

0.10.0 (2013-12.08)

0.9.7 (2013-08-09)

0.9.6 (2013-07-26)

0.9.5 (2013-07-18)

0.9.4 (2013-07-09)

0.9.3 (2013-06-12)

0.9.2 (2013-03-28)

0.9.1 (2013-02-13)

0.9.0 (2013-02-12)