Skip to content

feat(monitor): drive hyperlight-unikraft through hluk v0.14.1 - #1058

Open
danbugs wants to merge 11 commits into
urunc-dev:mainfrom
danbugs:hyperlight-unikraft-v0.14.0
Open

danbugs wants to merge 11 commits into
urunc-dev:mainfrom
danbugs:hyperlight-unikraft-v0.14.0

Conversation

@danbugs

@danbugs danbugs commented Sep 23, 2026

Copy link
Copy Markdown

hyperlight-unikraft now ships its host as a single hluk binary with an embedded Unikraft kernel. This moves the hyperlight-unikraft monitor onto that CLI (v0.14.1), following the contract upstream documents for urunc, and passes the container's command and environment through to the guest.

  • Monitor CLI: the binary looked up in PATH is hluk, run as hluk run --initrd … --scratch-mb …. Since hluk embeds its kernel, binary becomes optional for this monitor: an image without one boots the embedded kernel, one that ships a kernel has it passed as --kernel. The check stays in validate, so every other monitor behaves exactly as before, runc fallback included. A missing initrd is an error.
  • Guest command and environment: Unikraft.MonitorCli emits --guest-exec=<command> and one --env=KEY=VALUE per variable when the monitor is hyperlight-unikraft, the way it emits the 9p options for QEMU. Values are attached with = so a command starting with a dash is never read as an hluk option; an empty command leaves the guest to its conventional entrypoint.
  • Snapshots: a new com.urunc.unikernel.snapshot annotation names a directory saved by hluk snapshot save; urunc resumes it with hluk snapshot run <dir> instead of booting, with the same command and environment. Only valid on hyperlight-unikraft, and mutually exclusive with binary and initrd, since the snapshot carries both.
  • CI and e2e: the VM tests install the prebuilt hluk from the v0.14.1 release instead of building with cargo. The Hyperlight case runs ghcr.io/hyperlight-dev/hyperlight-unikraft/hello-urunc:v0.14.1, the image upstream publishes with every release, pinned to the version CI installs; you will probably want to point this at your own registry later. It replaces docker.io/urunc/hello-hyperlight-unikraft, whose 0.12.x kernel and driver-less initrd predate the current host. hluk prints guest output on stdout, so the stderr workaround in the ctr harness is gone.
  • Docs: installation and hypervisor-support pages describe hluk, how to install it, and how urunc maps annotations, command, memory and environment onto it; the version comes from versions.yml.

Verified locally on KVM with hluk v0.14.1: the e2e case, ctr run and docker run of the published image, container env visible inside the guest, an image with only an initrd (embedded kernel), and an image with only a snapshot. Networking and host mounts are not wired yet, as before; images built for 0.12.x need rebuilding on upstream's runtime rootfs images.

hyperlight-unikraft 0.13.0 rewrote its host as a single hluk binary and
0.14.0 builds on it, neither compatible with the 0.12.x command line
that urunc issued so far. Follow the new CLI:

- The binary urunc looks up in PATH is hluk, invoked as "hluk run".
- hluk embeds the Unikraft kernel it boots, so the unikernel binary of
  the image is no longer passed as the kernel. Only the initrd is, and
  building the command fails early when there is none.
- The --memory knob is gone. The memory of the container is passed as
  the scratch memory of the guest with --scratch-mb, in MiB.
- The options the unikernel asks for through MonitorCli are appended,
  like every other monitor does.

Add hluk to the spell-check dictionary.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
hluk boots its own kernel, so the kernel command line that Unikraft
builds for the other monitors never reaches the guest. hluk takes the
guest command through --guest-exec instead: a file inside the initrd
plus its arguments, which the runtime driver of the guest executes. An
empty command is not passed at all, which leaves the guest to its
conventional entrypoint.

Have Unikraft emit the option through MonitorCli when its monitor is
hyperlight-unikraft, the same way it emits the 9p options for QEMU. The
value is attached with "=", so hluk reads it as a single argument and a
command starting with a dash can never become an hluk option.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
On QEMU and Firecracker, Unikraft receives the environment variables of
the container through env.vars on the kernel command line. hluk takes
them as repeatable --env KEY=VALUE options, applied in the guest before
the command runs. Emit one per variable, attached with "=" like the
guest command, so a value is never read as an option.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
hyperlight-unikraft now publishes a prebuilt hluk for x86_64 Linux with
every release, so download it instead of building the host from source
with cargo. Bump the pinned version from v0.12.1 to v0.14.1 in the CI
and nightly workflows.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
Describe the hluk binary, its prebuilt release and crates.io installs,
and how urunc maps the annotations, the command line, the memory and
the environment of a container onto hluk run. Point the example at the
hello-urunc image of hyperlight-unikraft, which declares its own
command, and take the documented version from versions.yml, bumped to
0.14.1.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
The hello-hyperlight-unikraft image was built for hyperlight-unikraft
0.12.x: its kernel is not the one hluk boots and its initrd carries no
runtime driver, so hluk has nothing to dispatch the command to. Use the
hello-urunc image that hyperlight-unikraft publishes with every
release, pinned to the version CI installs. The image declares its own
command, so the case runs it as a user would, with nothing after the
image name, and that command still travels through --guest-exec.

hluk prints the guest output on stdout, so drop the workaround that
merged stderr into the output for Hyperlight and the helper it needed.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
hluk embeds a Unikraft kernel, but boots another one when asked with
--kernel. Pass the unikernel binary of the image there, so the binary
annotation keeps the meaning it has on every other monitor and an image
ships the kernel it was built and tested with. An empty unikernel path
is left to the embedded kernel. A kernel from another
hyperlight-unikraft release fails fast: hluk reports that the guest
halted without a word instead of hanging.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
hluk embeds the Unikraft kernel it boots, so an image for this monitor
has nothing to gain from shipping one, yet urunc rejected any image
without the binary annotation. Move the mandatory check from validate,
which only looks for the fields present, to validateValues, which knows
the monitor, and exempt hyperlight-unikraft: an image without a binary
boots the embedded kernel, one with a binary still has it passed as
--kernel. Every other monitor keeps requiring it, since the binary is
what they boot.

The block rootfs, which moves the binary out of the image, is
unreachable for this pair: Unikraft reports no block support.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
hluk can save a booted guest, kernel and rootfs included, as an OCI
layout directory and resume it later in place of a boot, with the
runtime already initialized. Let an image ship such a directory through
a new snapshot annotation, read from the spec or urunc.json, carried in
the container state like the initrd, and confined under the container
rootfs mount before the monitor command is built.

A snapshot stands in for both the binary and the initrd, and hluk
refuses either alongside it, so validation rejects an image that sets
the snapshot together with one of them, or on any monitor other than
hyperlight-unikraft. An image with only a snapshot gets no guest rootfs
from urunc, which still bind-mounts the container rootfs into the
monitor rootfs, where hluk reads the snapshot from.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
When the image carries a snapshot, run "hluk snapshot run" on it with
the guest command and environment the unikernel asks for, and nothing
else: hluk takes no kernel, initrd or scratch memory with a snapshot,
since the saved guest already holds them.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
Document com.urunc.unikernel.snapshot on the Hyperlight page and in the
list of optional annotations: what it replaces, how hluk resumes it and
that it is tied to the hluk release that saved it.

Signed-off-by: danbugs <danilochiarlone@gmail.com>
@netlify

netlify Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for urunc ready!

Name Link
🔨 Latest commit 3be3ce9
🔍 Latest deploy log https://app.netlify.com/projects/urunc/deploys/6ab42519e7c9ff00085a8f44
😎 Deploy Preview https://deploy-preview-1058--urunc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant