Skip to content

add microvm-rootfs and virtio-kernel-raw packages - #229

Merged
twitchyliquid64 merged 6 commits into
mainfrom
minvmd-rootfs-and-kernel-raw
Jun 8, 2026
Merged

add microvm-rootfs and virtio-kernel-raw packages#229
twitchyliquid64 merged 6 commits into
mainfrom
minvmd-rootfs-and-kernel-raw

Conversation

@norrietaylor

@norrietaylor norrietaylor commented Jun 7, 2026

Copy link
Copy Markdown
Member

Two packages for running a libkrun-based Linux microVM.

virtio-kernel-raw

Gunzips the virtio-linux Image.gz to an uncompressed aarch64 Image, which libkrun can load with KRUN_KERNEL_FORMAT_RAW — skipping its in-VMM gzip decompress for a faster microVM boot. build_deps: virtio-linux, gzip, bash, coreutils.

microvm-rootfs

A read-only ext4 guest rootfs: snapshots the closure of socat + bash + coreutils, adds a small vsock bring-up init (/sbin/microvm-init: writes a READY marker on vsock 7350, then serves an echo on vsock 2222) and an /etc/microvm/manifest contract, prunes build-only bulk, and packs an ext4 image (mke2fs -O ^has_journal) to load as a virtio-blk block device. build_deps: socat, bash, coreutils, e2fsprogs.

Validation

minimal check microvm-rootfs virtio-kernel-raw — all checkers pass (parse, import line, imports, fmt, naming, executable scripts, disallowed-patterns).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Produce raw virtio kernel images with automatic handling of compressed or uncompressed kernel artifacts.
    • Produce a read-only ext4 microVM root filesystem image suitable for microVM guests.
    • MicroVM rootfs includes a tiny init for readiness signaling, trimmed runtime contents, and automated sizing for packaging.

norrietaylor and others added 2 commits June 6, 2026 21:05
Gunzip the virtio-linux Image.gz to an uncompressed aarch64 Image so
consumers (minvmd) can load it with KRUN_KERNEL_FORMAT_RAW, skipping
libkrun's in-VMM gzip decompress (~77 ms, over half of microVM
boot-to-READY).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Assemble the minvmd microVM guest rootfs (socat + bash + coreutils
closure, bring-up init, /etc/minvmd/manifest boot contract) into a
read-only ext4 image via mke2fs. minvmd loads it as a block device
(krun_add_disk2 -> /dev/vda).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 47a15218-4bad-4f51-ae54-cb4befe50439

📥 Commits

Reviewing files that changed from the base of the PR and between f7025a7 and 3cd24e6.

📒 Files selected for processing (3)
  • packages/microvm-rootfs/build.ncl
  • packages/microvm-rootfs/build.sh
  • packages/virtio-kernel-raw/build.ncl
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/virtio-kernel-raw/build.ncl
  • packages/microvm-rootfs/build.sh

📝 Walkthrough

Walkthrough

Adds two new build packages: microvm-rootfs produces a journalless ext4 rootfs image from a staged runtime tree with a vsock init stub and manifest; virtio-kernel-raw produces a kernel Image by copying or decompressing an upstream vmlinuz.

Changes

microvm-rootfs guest filesystem image

Layer / File(s) Summary
Build specification and dependencies
packages/microvm-rootfs/build.ncl
NCL build spec that imports minimal.ncl, declares runtime and build_deps (base, socat, e2fsprogs, and local build.sh), sets cmd = "./build.sh", and exports usr/share/microvm-rootfs/rootfs.img.
Staging and init stub
packages/microvm-rootfs/build.sh
Snapshots runtime dirs into a staging tree, creates guest mountpoints, ensures /bin/sh, writes /sbin/microvm-init (vsock readiness retry + echo handler), and emits etc/microvm/manifest.
Image optimization and ext4 materialization
packages/microvm-rootfs/build.sh
Prunes build-time artifacts and static objects, validates mke2fs, computes image size with headroom, runs mke2fs -d to create a journalless ext4 rootfs.img, and verifies output size.

virtio-kernel-raw kernel image

Layer / File(s) Summary
Build specification and dependencies
packages/virtio-kernel-raw/build.ncl
NCL build spec that imports minimal.ncl, wires base and virtio-linux into build_deps with the local build.sh, sets cmd = "./build.sh" and build_args including version, and exports usr/share/virtio-linux/Image.
Kernel decompression
packages/virtio-kernel-raw/build.sh
Ensures $OUTPUT_DIR/usr/share/virtio-linux exists and writes Image by decompressing vmlinuz with gunzip -c when gzip-compressed or copying it unchanged otherwise.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • gominimal/pkgs#205: Adds the e2fsprogs package that provides mke2fs, which microvm-rootfs uses to create the ext4 image.

Suggested reviewers

  • mitodrummer

Poem

🐰 In staging fields the runtimes grow,

a tiny init says "ready" slow,
We prune and pack with mke2fs song,
A kernel Image joins along—
Two parcels bound, the build hops on.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding two new packages (microvm-rootfs and virtio-kernel-raw) to the repository.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch minvmd-rootfs-and-kernel-raw

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/minvmd-rootfs/build.sh`:
- Around line 18-23: The build script's directory-copy loop (for d in usr bin
sbin lib lib64 etc) copies the entire build sandbox into $STAGE, which pulls
build-only packages like e2fsprogs into the guest image; update the staging step
in build.sh to only include runtime closures (not build_deps) or explicitly
remove e2fsprogs outputs before mke2fs runs: either compute and copy the closure
of the runtime packages instead of blindly copying "/", or after the cp loop
delete the e2fsprogs outputs (and any other build_deps) from $STAGE so the final
rootfs.img matches the runtime dependency set declared in build.ncl and
build_deps, ensuring mke2fs uses only runtime artifacts.
- Around line 45-53: The init stub (minvmd-stub-init) currently always starts
the bridge listener even if the READY handshake loop never succeeds; change the
script to detect failure of the handshake (e.g., track a success flag or check
if i reached 50) and, if it never succeeded, print an error message to stderr
and exit with a non-zero status instead of proceeding to exec socat
VSOCK-LISTEN:2222,fork EXEC:cat; only exec the listener when the handshake loop
actually breaks/succeeds.
- Around line 71-76: The prune subshell currently swallows any failure because
the trailing "|| true" applies to the whole "( cd \"$STAGE\" && rm -rf ... &&
find ... )" sequence; change it so only the non-fatal find errors are ignored.
Concretely, keep "cd \"$STAGE\"" and "rm -rf usr/include ..." as-is so failures
propagate, and wrap or append "|| true" only to the find invocation (e.g. ( cd
\"$STAGE\" && rm -rf ... && (find . \( -name '*.a' -o -name '*.la' -o -name
'*.o' \) -delete 2>/dev/null || true) )), ensuring STAGE, the cd command, and
the rm -rf invocation will fail the script while only find's noncritical errors
are tolerated.

In `@packages/virtio-kernel-raw/build.ncl`:
- Around line 6-25: This BuildSpec for name = "virtio-kernel-raw" is missing the
required version plumbing: add a top-level binding for version and set
attrs.upstream_version to that value, and forward the version into the build via
build_args (e.g., include version in build_args) so the spec conforms to the
repo contract; update the BuildSpec to include a version binding, add
attrs.upstream_version referencing version, and add build_args that
pass/version-include the version into the build process so BuildSpec consumers
get the upstream_version and version forwarded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 878467e7-07a7-4145-a51a-9335a87ef51b

📥 Commits

Reviewing files that changed from the base of the PR and between 133db0c and d9e9f7c.

📒 Files selected for processing (4)
  • packages/minvmd-rootfs/build.ncl
  • packages/minvmd-rootfs/build.sh
  • packages/virtio-kernel-raw/build.ncl
  • packages/virtio-kernel-raw/build.sh

Comment thread packages/microvm-rootfs/build.sh
Comment thread packages/minvmd-rootfs/build.sh Outdated
Comment on lines +45 to +53
cat > "$STAGE/sbin/minvmd-stub-init" <<'STUB'
#!/bin/sh
i=0
while [ "$i" -lt 50 ]; do
printf 'READY\n' | socat -t2 - VSOCK-CONNECT:2:7350 && break
i=$((i + 1))
sleep 0.1
done
exec socat VSOCK-LISTEN:2222,fork EXEC:cat

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail the init stub when the READY handshake never succeeds.

If all 50 attempts miss, PID 1 still starts the bridge listener and the guest never emits the documented READY signal on port 7350. That turns a bring-up failure into a downstream timeout instead of an immediate, diagnosable boot failure.

Suggested fix
 i=0
 while [ "$i" -lt 50 ]; do
     printf 'READY\n' | socat -t2 - VSOCK-CONNECT:2:7350 && break
     i=$((i + 1))
     sleep 0.1
 done
+[ "$i" -lt 50 ] || {
+    echo "ERROR: failed to publish READY on vsock 7350" >&2
+    exit 1
+}
 exec socat VSOCK-LISTEN:2222,fork EXEC:cat
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
cat > "$STAGE/sbin/minvmd-stub-init" <<'STUB'
#!/bin/sh
i=0
while [ "$i" -lt 50 ]; do
printf 'READY\n' | socat -t2 - VSOCK-CONNECT:2:7350 && break
i=$((i + 1))
sleep 0.1
done
exec socat VSOCK-LISTEN:2222,fork EXEC:cat
cat > "$STAGE/sbin/minvmd-stub-init" <<'STUB'
#!/bin/sh
i=0
while [ "$i" -lt 50 ]; do
printf 'READY\n' | socat -t2 - VSOCK-CONNECT:2:7350 && break
i=$((i + 1))
sleep 0.1
done
[ "$i" -lt 50 ] || {
echo "ERROR: failed to publish READY on vsock 7350" >&2
exit 1
}
exec socat VSOCK-LISTEN:2222,fork EXEC:cat
STUB
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/minvmd-rootfs/build.sh` around lines 45 - 53, The init stub
(minvmd-stub-init) currently always starts the bridge listener even if the READY
handshake loop never succeeds; change the script to detect failure of the
handshake (e.g., track a success flag or check if i reached 50) and, if it never
succeeded, print an error message to stderr and exit with a non-zero status
instead of proceeding to exec socat VSOCK-LISTEN:2222,fork EXEC:cat; only exec
the listener when the handshake loop actually breaks/succeeds.

Comment thread packages/microvm-rootfs/build.sh Outdated
Comment thread packages/virtio-kernel-raw/build.ncl
…rences

Make the packages product-agnostic: microvm-rootfs with /sbin/microvm-init
and /etc/microvm/manifest, and scrub internal references from comments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@norrietaylor norrietaylor changed the title add minvmd-rootfs and virtio-kernel-raw packages add microvm-rootfs and virtio-kernel-raw packages Jun 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
packages/microvm-rootfs/build.sh (2)

17-22: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Build-only dependency content is still being shipped in the guest image (Line 17).

The stage copy from / pulls e2fsprogs into the final rootfs, even though it is described as build-time only. This bloats the image and breaks the build/runtime dependency split.

As per coding guidelines, "Declare packages in 'build_deps' if needed during the build but not during package deployment."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/microvm-rootfs/build.sh` around lines 17 - 22, The current for-loop
(for d in usr bin sbin lib lib64 etc; do ... cp -a "/$d" "$STAGE/") copies
entire host dirs into $STAGE and pulls build-only packages like e2fsprogs into
the image; change the copy to either use rsync/cp with explicit exclude patterns
(e.g., exclude paths/filenames owned by e2fsprogs such as /sbin/mke2fs,
/sbin/resize2fs, /usr/sbin/*e2fsprogs*) or perform the same cp-then-prune
approach by removing those specific binaries from $STAGE right after the loop,
and ensure those packages are declared as build_deps instead of runtime deps.
Target symbols to edit: the for-loop that runs cp -a and the $STAGE cleanup (or
replace with rsync --exclude) so build-only files are not present in the final
rootfs.

Source: Coding guidelines


70-75: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

|| true masks prune/setup failures beyond find (Line 70).

Because || true is attached to the full chained expression, failures in cd "$STAGE" or rm -rf ... can be swallowed, producing a silently wrong image.

Suggested fix
-( cd "$STAGE" && \
-  rm -rf usr/include usr/share/man usr/share/doc usr/share/info \
-         usr/share/locale usr/share/i18n usr/lib/locale usr/lib/pkgconfig \
-         usr/share/aclocal usr/share/gtk-doc usr/share/bash-completion \
-         usr/share/gdb && \
-  find . \( -name '*.a' -o -name '*.la' -o -name '*.o' \) -delete 2>/dev/null || true )
+(
+  cd "$STAGE"
+  rm -rf usr/include usr/share/man usr/share/doc usr/share/info \
+         usr/share/locale usr/share/i18n usr/lib/locale usr/lib/pkgconfig \
+         usr/share/aclocal usr/share/gtk-doc usr/share/bash-completion \
+         usr/share/gdb
+  find . \( -name '*.a' -o -name '*.la' -o -name '*.o' \) -delete 2>/dev/null || true
+)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/microvm-rootfs/build.sh` around lines 70 - 75, The chained command
"( cd \"$STAGE\" && rm -rf ... && find . ... -delete 2>/dev/null || true )"
currently swallows failures from cd or rm because the trailing "|| true" applies
to the whole chain; change it so only the find's non-critical failures are
ignored and earlier failures propagate: run cd "$STAGE" and the rm -rf operation
(the "cd \"$STAGE\"" and "rm -rf usr/include ... usr/share/gdb" parts) such that
they error out on failure (keep the && between them), then run the find command
separately and append "|| true" only to the find (or suppress only its stderr)
so that only find's errors are tolerated; ensure the commands referencing STAGE,
cd "$STAGE", rm -rf, and find . \( -name '*.a' -o -name '*.la' -o -name '*.o' \)
-delete are updated accordingly so failures in cd or rm cause the script to exit
non-zero.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/microvm-rootfs/build.sh`:
- Around line 17-22: The current for-loop (for d in usr bin sbin lib lib64 etc;
do ... cp -a "/$d" "$STAGE/") copies entire host dirs into $STAGE and pulls
build-only packages like e2fsprogs into the image; change the copy to either use
rsync/cp with explicit exclude patterns (e.g., exclude paths/filenames owned by
e2fsprogs such as /sbin/mke2fs, /sbin/resize2fs, /usr/sbin/*e2fsprogs*) or
perform the same cp-then-prune approach by removing those specific binaries from
$STAGE right after the loop, and ensure those packages are declared as
build_deps instead of runtime deps. Target symbols to edit: the for-loop that
runs cp -a and the $STAGE cleanup (or replace with rsync --exclude) so
build-only files are not present in the final rootfs.
- Around line 70-75: The chained command "( cd \"$STAGE\" && rm -rf ... && find
. ... -delete 2>/dev/null || true )" currently swallows failures from cd or rm
because the trailing "|| true" applies to the whole chain; change it so only the
find's non-critical failures are ignored and earlier failures propagate: run cd
"$STAGE" and the rm -rf operation (the "cd \"$STAGE\"" and "rm -rf usr/include
... usr/share/gdb" parts) such that they error out on failure (keep the &&
between them), then run the find command separately and append "|| true" only to
the find (or suppress only its stderr) so that only find's errors are tolerated;
ensure the commands referencing STAGE, cd "$STAGE", rm -rf, and find . \( -name
'*.a' -o -name '*.la' -o -name '*.o' \) -delete are updated accordingly so
failures in cd or rm cause the script to exit non-zero.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8ba0f581-3ae0-4438-b12f-668b0776fe91

📥 Commits

Reviewing files that changed from the base of the PR and between d9e9f7c and 96a30e3.

📒 Files selected for processing (3)
  • packages/microvm-rootfs/build.ncl
  • packages/microvm-rootfs/build.sh
  • packages/virtio-kernel-raw/build.ncl
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/virtio-kernel-raw/build.ncl

The kernel is only gzip-compressed on aarch64 (Image.gz); on x86_64
virtio-linux ships a bzImage, so the unconditional gunzip failed the
build there. gzip-test first: decompress when gzip, else pass through.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/microvm-rootfs/build.ncl Outdated
# block device.
build_deps = [
{ file = "build.sh" } | Local,
socat,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is socat used for in the build script?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is placeholder will replace it later.

Comment thread packages/microvm-rootfs/build.ncl Outdated
build_deps = [
{ file = "build.sh" } | Local,
socat,
bash,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might make sense to depend on base instead for build_deps

coreutils,
e2fsprogs,
],

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be more pure to depend on the things you need in the rootfs image as runtime_deps, as that’s kinda the vibe of runtime deps. Sounds like you probably want base, socat, core utils as runtime deps and everything else as build deps, but note in practice the union of both will show up in your rootfs.

@twitchyliquid64 twitchyliquid64 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ll approve it tho so your g2g when you’re happy with it

Address review feedback:
- Declare base + socat as runtime_deps and e2fsprogs as a build-only dep
  (mke2fs packs the image), instead of lumping everything in build_deps.
- Remove the build-only e2fsprogs files from the staged tree before packing,
  so the runtime image carries only the runtime closure (mke2fs runs from the
  build sandbox PATH, not from $STAGE). Verified absent from rootfs.img.
- Fail the bring-up init if the READY handshake never succeeds, rather than
  starting the listener and turning a boot failure into a downstream timeout.
- Scope `|| true` to the find invocation so a cd/rm failure still fails the build.
Add the version binding, attrs.upstream_version, and build_args forwarding the
repo's build.ncl contract expects. Depend on base (provides gzip + sh at build)
instead of listing gzip/bash/coreutils individually.
@norrietaylor

Copy link
Copy Markdown
Member Author

Addressed the feedback (commits 7133c1e, 3cd24e6):

@twitchyliquid64 (dep hygiene)microvm-rootfs now declares runtime_deps = [base, socat] and build_deps = [build.sh, e2fsprogs] (e2fsprogs is build-only: it provides mke2fs to pack the image). base covers bash/coreutils/gzip.

CodeRabbit:

  • Build-only tools in the image — the staged tree drops the e2fsprogs files (usr/sbin, the few usr/bin tools, libext2fs/libe2p/libss) before mke2fs runs (which executes from the sandbox PATH, not $STAGE). Verified against the built image: no mke2fs/e2fsck in /usr/sbin, e2fs libs gone, socat/microvm-init/bin/sh intact.
  • Init never failing on a missed READY — added [ "$i" -lt 50 ] || { echo …; exit 1; } before the listener exec.
  • || true swallowing the whole prune block — scoped to the find only; cd/rm -rf failures now fail the build.
  • virtio-kernel-raw missing version plumbing — added version, attrs.upstream_version, and build_args { include version }.

Both packages minimal package --rebuild clean.

@twitchyliquid64
twitchyliquid64 added this pull request to the merge queue Jun 8, 2026
Merged via the queue into main with commit 9610b7b Jun 8, 2026
4 checks passed
@twitchyliquid64
twitchyliquid64 deleted the minvmd-rootfs-and-kernel-raw branch June 8, 2026 00:11
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.

2 participants