Getting to the Heart of Unikernels
One self-contained image from microcontroller to microVM to the edge, and why that posture is a new normal for systems programming
flowchart LR
A0["reset vector,<br/>registers beneath"] --> A1["hypervisor<br/>virtual devices"] --> A2["host kernel<br/>syscall surface"] --> A3["command queue,<br/>compiled graph"] --> A4["reconfigurable<br/>fabric"]
A0 -.- MCU["Microcontroller"]
A1 -.- UVM["MicroVM"]
A2 -.- CON["Container"]
A3 -.- ACC["GPU / NPU / CGRA"]
A4 -.- FAB["FPGA / ASIC"]
classDef axis fill:#2a2a2a,stroke:#888,color:#ddd;
classDef target fill:#1a2a3a,stroke:#48a,color:#cdf;
class A0,A1,A2,A3,A4 axis;
class MCU,UVM,CON,ACC,FAB target;
Left to right is the platform, from a bare reset vector to a full hardware fabric. The sealed-artifact posture stays constant across the line; only the grant and the app label changes. “Unikernel” is the CPU, microVM, and container word for the sealed image with no resident software host beneath it. The spatial end keeps its own vocabulary (a GPU kernel dispatched in warps, an FPGA bitstream, a CGRA configuration). The word is also distinct from the freestanding compilation, detailed in the entry below.
A typical workload runs on top of a general-purpose operating system: a kernel, a driver stack from whichever vendors supplied the hardware, a package manager, a shell, and a userland context that aims to simplify some things about the landscape while complicating others. Most of that stack sits idle for the workload’s entire life. It still ships in the image, still gets patched on its own schedule, and still represents code that’s available. That is the ordinary, realistic shape of most software today, not a hypothetical worst case; a dynamic linker resolving a shared library at start time, a driver with its own update cadence, a shell reachable if anything goes wrong upstream. None of it is exotic risk. It is just what “runs on an operating system” has meant for decades.
The unikernel concept is one answer to that shape. Seal the computation graph down to exactly what a workload needs, statically coupled at build time, with no software host stack resident beneath it, and most of that idle surface simply is not there to patch, exploit, or wait on. The image has inherent advantages such as faster cold start times because it skips the layers that ordinary boot traverses, and it is smaller because nothing unused was ever linked in. That combination, less to attack and less to boot, is why the concept keeps resurfacing across a wide range of deployment shapes, from containers and microVMs down to workloads built for a single piece of hardware. Our framework treats the artifact class as a category with normative language of its own in the spec, and we are designing the deployment story to span that same range end to end.
The word itself carries baggage the definition does not support. When unikernels come up in conversation, the mental image tends toward the hair shirt: one core, one thread, no allocator, a workload squeezed onto hardware that leaves no other choice. That impression mistakes the first demonstration hardware for the class. A unikernel is a single, self-contained artifact, sealed at build time, with no software stack resident beneath it inside its own boundary. Core count is a property of the target it lands on. So are thread count, memory strategy, and whether a C library is linked. None of them belong to the term.
One Sealed Image
The “uni” names the artifact, not its capability. In the library-OS lineage the unikernel descends from, the services an operating system would provide become libraries the application links against, and the build emits one object that contains the application together with everything it needs to run. One sealed unit, fixed at build time, with no software stack resident beneath it. That is the whole definition. It says nothing about how many cores the artifact may use, how many threads it may run, or what its memory discipline looks like once control arrives; those are properties of the target it lands on, not of the class. Our spec draws the same line between the freestanding compilation a unikernel requires and everything else a specific target may or may not grant.
flowchart TB
subgraph SEAL["Sealed image"]
direction TB
SA["Application"] --> SL["C library (musl / glibc), statically coupled, only what is used"]
SL --> SK["kernel / bare hardware"]
end
subgraph GEN["General-purpose OS"]
direction TB
GA["Application"] --> GL["C library, dynamically linked"]
GL --> GU["shell, package manager, userland"]
GU --> GD["vendor driver stack"]
GD --> GK["kernel"]
end
classDef idle fill:#2a2a2a,stroke:#888,color:#bbb,stroke-dasharray:4 3;
classDef present fill:#1a2a3a,stroke:#48a,color:#cdf;
class GU,GD idle;
class GA,GL,GK,SA,SL,SK present;
The dashed layers on the left ship, get patched, and hold privilege for the workload’s whole life while sitting idle. The sealed image links only what the application uses, so those layers are not present to patch, exploit, or wait on.
Most toolchains can already produce a freestanding artifact; that half of the problem was solved a long time ago. What has kept unikernels a specialist’s tool is everything past that: hand-written service layers standing in for the OS pieces a workload still needs, one hypervisor’s guest format at a time, and no path back to the ordinary developer workflow once a team commits. Fidelity approaches this from the compiler down rather than the binary up. The same Clef source that targets a hosted Linux process today would target a sealed artifact by changing what the Platform Descriptor declares, not by rewriting the program. Static coupling, the discipline that removes the dynamic-linking seam described above, falls out of that same freestanding compilation as one build path. And the actor-and-arena memory discipline we are designing for every target, deterministic lifetimes tied to actor scope rather than a garbage collector, is exactly the discipline a sealed build needs to run without a runtime underneath it. A unikernel is difficult to reach when the compiler was designed around a hosted assumption and the sealed form has to be bolted on after the fact. It is a natural target when the compiler is being built to make no such assumption in the first place.
Flexing Without musl
The sealed-image diagram shows a C library statically linked into the image, and musl is the usual choice there: it is small, static-link-friendly, and already the libc behind most scratch-container and unikernel builds. But a statically linked libc is a convenience, not a floor. It is one more thing the image can carry, not something the artifact class requires.
Fidelity compiles Clef to native code directly rather than through a C ABI, so a sealed image can drop the C runtime entirely. The spec draws this as a third linkage tier past hosted-dynamic and freestanding-static: a bare target with no C runtime at all, where no libc is linked, there is no foreign-function boundary, and interior memory follows the compiler’s own lifetime lattice rather than malloc and free. On such a target the arena and static-storage discipline is the whole memory model; there is no allocator to call because none was linked.
This is not a corner case for us. Our first prototype is already there. The bare-metal M33 credential build links no libc, has no heap region, and enters at the reset vector with no C startup code between the silicon and the credential logic. Where a conventional unikernel toolchain still assumes some libc underneath the application, a Clef image treats the C library as one optional layer among several the platform declaration may or may not include. Dropping it removes the last general-purpose runtime component from the image, and with it the last piece of attack surface and startup cost that the application never asked for.
Prior Art and What Survived It
MirageOS is the prior-art waypoint. The 2013 paper Unikernels: Library Operating Systems for the Cloud made the founding argument: specialize the build to the application, link only what the application uses, and boot the result directly as a guest. The part of that project which demonstrably survived into the wild is the networking. Docker Desktop ships VPNKit, a service assembled from Mirage networking libraries, and it has routed container traffic on developer machines for a decade. The ecosystem that followed broadened the substrate choices: Unikraft reports millisecond-scale boots for specialized images, and the unikernels-as-processes line of research showed the artifact running as an ordinary process behind a narrow syscall filter, no hypervisor required.
It is fair to ask why the 2010s wave receded without taking over the cloud. The objections were practical. Debugging norms assumed a shell in the image; observability assumed an agent; patching assumed a package manager. Linux userland gravity is real, and a decade ago the operational tooling demanded that teams give up more than the smaller footprint gave back.
Mainstream practice then spent ten years moving toward the unikernel’s assumptions. Immutable infrastructure became the default posture of serious shops, and patch-by-rebuild stopped being an objection once CI pipelines redeployed on every merge as routine. Scratch-based container images holding a single static binary went from curiosity to recognized supply-chain practice. The industry adopted, piece by piece, the operational model unikernels required all at once.
We took inspiration from Mirage early in our design work, and we made a different set of commitments. A library OS binds the sealed image to a language runtime and to hypervisor guests, and that keeps deployment on CPU-shaped substrates. Our rollout starts from the compiler instead. The freestanding discipline and the platform declaration carry the same artifact posture to bare metal in our first prototype, and they are designed to carry it across containers, microVMs, and further down the accelerator landscape.
From Freestanding Builds to Sealed Images
Our compilation story starts from the freestanding discipline and builds toward the sealed image. The spec defines two freestanding entry modes: a hosted-ELF Linux form that carries no libc and terminates through the exit syscall, and a bare-metal form for Cortex-M33 class parts that enters at the reset vector with no loader and no syscall ABI at all. The work on STM32-class hardware is where the small end of that story lives, and reactive pipelines for sensor-class deployments are part of how we think about those targets.
On the M33, the Platform Descriptor declares a 4-byte word with no heap region, entered at the reset vector, and the compiler’s contract is to commit to exactly that. Nothing about the mechanism is small-end specific: a container definition that grants eight cores and a gigabyte of memory under a narrow syscall policy is also a platform declaration, so the same compiler that produces the M33 prototype would produce one that runs multi-threaded Olivier actors with arena-backed, RAII-disciplined memory inside that larger grant. The actor model and its orchestration layer remain design-stage work; the claim we make plainly is architectural, that the artifact class carries no capability ceiling of its own.
Our first concrete unikernel implementation is the bare-metal M33 build for our post-quantum credential prototype. A credential device is where the static-coupling argument stops being abstract: the artifact carries every element necessary to satisfy its remit and nothing beyond that, so there is no linker to redirect, no shell to land in, and no idle machinery for a fault to hide inside. In essence, the application is the operating system. Control arrives at the reset vector and lands directly in credential logic. The credential authority chapter specifies the design; the prototype puts the sealed-image properties to work on real silicon.
The Cold-Start Collapse
Deployment substrates for a sealed image span three forms today, and the useful comparison is who provides the machine:
| Substrate | Who provides the machine | What the image assumes |
|---|---|---|
| Bare metal | The silicon itself | Nothing; entry at the reset vector |
| MicroVM (Firecracker-class) | A hypervisor’s virtual hardware | Virtual devices; the image brings its own service layer |
| Container (LXC-style, scratch image) | The host kernel’s syscall interface | Syscalls within the granted policy; empty userland |
Purists sometimes reserve the word for the middle row, where the image brings its own kernel-role code as a guest. We read the class by its properties instead: one sealed artifact with an empty userland and no resident stack inside its boundary. The process form carries those properties intact, which is the argument the unikernels-as-processes research made formally, and it is the form that matters most for where deployment platforms are heading.
Cold start is where the arithmetic turns visible. A conventional container cold start does work a sealed artifact never generates: it pulls layered filesystems, starts an init process, walks the dynamic linker across shared objects, and then warms whatever runtime the application may require. A Clef-compiled unikernel would skip nearly all of that. There is always hardware bring-up, but that cost is bounded by what the unikernel actually needs, not the full menu of a general-purpose operating system: one small static binary, nothing in it needing interpretation or JIT warmup, no linker pass at entry, and arena-based memory that skips garbage-collector initialization. Entry is a jump. AWS built Firecracker to boot minimal microVMs in roughly a hundred milliseconds, and it runs underneath Lambda; boots measured in single-digit milliseconds inside that same harness appear throughout the unikernel literature. The floor keeps dropping as the artifact approaches the application and nothing else.
flowchart LR
subgraph CONV["Conventional container cold start"]
direction LR
C1["pull layered<br/>filesystem"] --> C2["start init<br/>process"] --> C3["walk dynamic<br/>linker"] --> C4["warm runtime<br/>(JIT / GC)"] --> C5["run"]
end
subgraph SEAL["Sealed image cold start"]
direction LR
S1["hardware<br/>bring-up"] --> S2["jump to entry"] --> S3["run"]
end
classDef paid fill:#2a2a2a,stroke:#888,color:#bbb,stroke-dasharray:4 3;
classDef fast fill:#1a2a3a,stroke:#48a,color:#cdf;
class C1,C2,C3,C4 paid;
class C5,S1,S2,S3 fast;
Each dashed step on the top row is work a sealed image never generates. Bring-up on the bottom row is bounded by what the workload declares it needs, not the full menu of a general-purpose boot.
That arithmetic drew us to Cloudflare’s Container infrastructure and shaped the hybrid we sketched in Unexpected Fusion: CloudEdge actors for coordination, Fidelity-compiled unikernels for compute-dense work. Near-instant cold starts would make scale-to-zero the default posture rather than a compromise, and dense deployment of minimal-overhead images points at a cost model that recasts applications the current economics rule out. Fan-out across instances would ride on BAREWire-framed messages, the coordination model our JavaScript-targeting design lays out for compute that outgrows one instance. Firecracker and LXC-style substrates would take the same build with different isolation trade-offs, which is the practical benefit of an artifact class defined by self-containment: the substrate becomes a deployment decision instead of a build decision.
Leveling the Horizon Line
From our design perspective, there is a question we keep circling: where does the concept stop? We theorize in this section, and hold the taxonomy loosely.
FPGA and dedicated-IC practice has always worked the way this entry describes. A bitstream is a sealed artifact that configures the fabric and becomes the system; nobody asks what operating system the fabric runs, because the question has no referent. Our hardware-inference work sits in that tradition. The dedicated-silicon world never needed the unikernel concept because it never had the operating-system assumption to subtract.
General-purpose accelerators sit closer to that world than their CPU-adjacent position suggests. The unit of work handed to a GPU is already called a kernel, and the scheduling vocabulary around it, warps and command queues and streams, describes hardware-managed dispatch with no OS norms anywhere in it. Unified-memory APUs pull those workloads into the CPU’s address space without adding an operating system between the workload and the compute units. NPUs accept compiled graphs. CGRAs accept configurations. Each device takes a sealed artifact, ignites it, and lets it own its granted resources to completion.
The wrinkle is the igniter. These workloads depend on a bootstrapping orchestrator, usually a CPU, and that dependence is the obvious objection to counting them in the class. We think the bootloader analogy carries the weight here. An M33 build receives control from a boot ROM that then steps out of the way, and nobody counts the boot ROM as a host. The line we find useful is residence rather than ignition: whether a software host stays underneath the workload during execution serving it, or hands over at entry and disappears. A GPU kernel that runs to completion on its compute units sits on the sealed-image side of that line. A workload that round-trips through a driver mid-flight sits on the other. Reasonable people could draw the line elsewhere, and we expect our own position to sharpen as the compilation work reaches those targets.
Whether or not the term stretches that far, the leveling is what we are after. Spatial and dedicated processors never reflected operating-system norms, and the sealed image brings general-purpose deployment into the same frame. One conceptual horizon line runs flat across the landscape, from microcontroller and microVM to container and fabric, and the question at every point on it is the same: what did the platform declare, and what does the artifact assume?
A New Normal for Dedicated Systems
The practical value lands first. A sealed artifact deploys into general-purpose environments with near-instant startup and a security posture that closes the dynamic-linking seam and strips out the userland an intruder would live off. It widens the menu of substrates a workload can land on, because self-containment makes the substrate a late decision. And in our design, capability is bounded by the platform declaration, so the eight-core container build and the single-core sensor node would be the same kind of thing at two points on one spectrum.
The framing we want to leave behind is the corner. Unikernels read as a niche only when the operating system is treated as the natural center of gravity, and dedicated systems programming never granted that premise. FPGA and ASIC work treats the artifact as the system as a matter of course, and compilation for spatial processors is growing in the same soil. General-purpose CPUs are late arrivals to a posture the rest of the hardware landscape considers ordinary, and the tooling shift of the last decade says the arrival is well underway.
We build freestanding images today, at the small end where the discipline is unavoidable and on hosted Linux where it is a choice. The sealed-container story is where our CloudEdge design work is headed, and the horizon-leveling question, how far down the accelerator landscape one compilation discipline can reach, is one we expect to keep working at in the open. There is more to come as that work matures with our customers.
Related Entries
- Getting the Signal with BAREWire: reactive programming in Clef without subscription overhead, one signal model across native, web, and managed targets
- RAII in Olivier and Prospero: actor-aware memory management through deterministic lifetimes, with each actor owning an arena that manages its own lifetime without adverse overhead
- Arena Hoisting: the Lattice analyzer that trades a runtime guard for a compile-time arena placement, keeping the safe default and surfacing the faster choice
- Unexpected Fusion: how OCaml and Erlang inspired an agentic systems architecture informs Fidelity’s continuation passing core