Skip to content

microvm-rootfs: ship e2fsprogs + util-linux and /var/lib/minimal mountpoint - #365

Merged
norrietaylor merged 3 commits into
mainfrom
microvm-rootfs-writable-volume-support
Jul 8, 2026
Merged

microvm-rootfs: ship e2fsprogs + util-linux and /var/lib/minimal mountpoint#365
norrietaylor merged 3 commits into
mainfrom
microvm-rootfs-writable-volume-support

Conversation

@norrietaylor

@norrietaylor norrietaylor commented Jul 8, 2026

Copy link
Copy Markdown
Member

What

Adds the three pieces the guest rootfs needs to support a per-VM writable ext4 volume, unblocking Unit 1 of the per-VM writable volume spec (gominimal/minimal#583, PR #658 R1.7).

The in-guest minimald mounts a per-VM writable volume (/dev/vdb) at /var/lib/minimal, formats it with mkfs.ext4 on first boot, and reclaims space via discard / fstrim. The current rootfs (base + git) ships none of these. This change:

  1. Promotes e2fsprogs from build-only to the runtime closure. It was previously a build_deps entry whose files (usr/sbin, mke2fs, e2fsprogs libs) were deliberately stripped from the image by build.sh. Now its files ship, providing mkfs.ext4/mke2fs in the guest. mke2fs remains on the build PATH via the injected runtime closure, so it still packs the image — the redundant build_deps entry and the strip block are removed.
  2. Adds util-linux to the runtime closure for fstrim (space reclaim). mount -o discard (online discard) also works with plain ext4, so this is secondary to e2fsprogs.
  3. Stages /var/lib/minimal as an empty directory. The root image is mounted read-only in the guest, so minimald cannot mkdir the mountpoint at runtime — it must ship in the image or the mount fails with ENOENT/EROFS.
  4. Canonicalizes libblkid/libuuid to util-linux's versioned libs. e2fsprogs and util-linux both ship a libblkid.so.1/libuuid.so.1 with the same soname; staging resolved them to e2fsprogs's older, unversioned forks, so util-linux's libmount warned no version information available on every fstrim/mount/blkid. build.sh now repoints both sonames at util-linux's versioned libs (an ABI superset — e2fsprogs's own mke2fs/e2fsck link them fine) and drops the e2fsprogs forks. A guard fails the build if a package bump renames the util-linux targets, so it can't silently regress.

No packaging/format change: the output stays a raw ext4 rootfs.img.

Verification

Built with minimal patched-build microvm-rootfs, then mounted the resulting ext4 image (loopback, in a Linux container) and chrooted:

  • /var/lib/minimal — present, empty (0 entries).
  • mkfs.ext4 -V / mke2fs -Vmke2fs 1.47.4. mkfs.ext4 is the mke2fs symlink in /usr/sbin; a real mkfs.ext4 -F format of a scratch image succeeds against util-linux's libblkid.
  • fstrim --help / fstrim --versionfstrim from util-linux 2.42.1, zero stderr (no libblkid warning).
  • mount -Vmount from util-linux 2.42.1, zero stderr.
  • libblkid.so.1 -> libblkid.so.1.1.0 and libuuid.so.1 -> libuuid.so.1.3.0 (util-linux); e2fsprogs forks removed.
  • blkid on the image → TYPE="ext4" (raw ext4, boot layout unchanged).
  • min check --packages microvm-rootfs — all checks Pass (including post-build enumerate bins, output types valid, missing runtime_deps).

Image size: 444,070,912471,600,128 bytes (+~27.5 MiB), bounded and expected for both packages' binaries + libs. (The bulk of the image — a ~192 MiB GCC in the closure — is pre-existing in both baseline and this build; not introduced here.)

Follow-up (not in this PR)

The scoped libblkid/libuuid repoint above fixes the warning for this image. The upstream-correct fix is to build e2fsprogs with --disable-libblkid --disable-libuuid --disable-uuidd so util-linux is the sole provider ecosystem-wide (and the guest gets util-linux's modern blkid binary too, rather than e2fsprogs's 2003-era one). That touches a foundational package and forces a rebuild + re-pin of every e2fsprogs consumer, so it's deliberately out of scope here. Worth filing separately.

⚠️ Downstream re-pin required (cross-repo handoff)

Once this merges and the microvm-rootfs artifact is published, gominimal/minimal must re-pin it in .minimal/minimal.toml under [outputs.minvmd-rootfs] (locked_commit) to the new build, so the updated rootfs (with /var/lib/minimal, mkfs.ext4, fstrim) is materialized. The guest mount code (minimald mount_state_volume) and host attach already exist in gominimal/minimal and are waiting on this rootfs.

Refs: gominimal/minimal#583, gominimal/minimal#658

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Prevented per-VM writable volume mount failures by ensuring the /var/lib/minimal directory exists during staging under a read-only root.
    • Improved runtime disk-space reclamation support by aligning the expected libblkid/libuuid libraries to the provided util-linux versions, with build-time validation.
    • Updated the “missing tool” guidance to point to the correct runtime dependency for filesystem creation.
  • Documentation
    • Refreshed microvm-rootfs build notes to reflect the updated runtime contents (including util-linux) and support for reclaiming space via fstrim.

…tpoint

Unblocks Unit 1 of the per-VM writable ext4 volume spec (gominimal/minimal
#583, PR #658 R1.7). The guest minimald mounts a per-VM writable volume
(/dev/vdb) at /var/lib/minimal, formats it with mkfs.ext4 on first boot, and
reclaims space via discard/fstrim. The rootfs was missing all three pieces:

- Promote e2fsprogs from a build-only dep (its files were stripped from the
  image) to the runtime closure, so mkfs.ext4/mke2fs ship in the guest. mke2fs
  stays on the build PATH via the injected runtime closure, so it still packs
  the image — the separate build_deps entry is now redundant and removed.
- Add util-linux to the runtime closure for fstrim (space reclaim).
- Stage /var/lib/minimal as an empty directory. The root is mounted read-only,
  so minimald cannot mkdir the mountpoint at runtime; it must ship in the image
  or the mount fails with ENOENT/EROFS.

Image stays raw ext4 (rootfs.img); size grows ~26 MiB (from ~444 MB to
~472 MB). Verified in the built image: /var/lib/minimal is empty, mkfs.ext4 -V
and fstrim --help resolve, filesystem TYPE=ext4.

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

coderabbitai Bot commented Jul 8, 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: 3fecbe24-fd1c-4fec-9f25-d7436ac1092a

📥 Commits

Reviewing files that changed from the base of the PR and between f2cf30b and d7077bc.

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

📝 Walkthrough

Walkthrough

Adds util-linux and e2fsprogs to the microvm-rootfs runtime closure, prepares the writable mountpoint during staging, canonicalizes libblkid/libuuid sonames to util-linux versions, and updates related comments and the mke2fs error text.

Changes

microvm-rootfs util-linux and writable-volume support

Layer / File(s) Summary
Runtime dependency spec update
packages/microvm-rootfs/build.ncl
Imports util-linux, adds it to runtime_deps, and updates the description comment to mention fstrim for volume reclamation.
Staging and mountpoint setup
packages/microvm-rootfs/build.sh
Updates the header documentation, creates $STAGE/var/lib/minimal in the staged image, and changes the mke2fs missing-binary message to reference e2fsprogs in runtime_deps.
Library soname canonicalization
packages/microvm-rootfs/build.sh
Checks for util-linux versioned libblkid and libuuid files, removes the e2fsprogs fork sonames, and symlinks the util-linux sonames into place.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • gominimal/pkgs#229: Introduced the microvm-rootfs package that this PR extends in both build.ncl and build.sh.
  • gominimal/pkgs#298: Also updates microvm-rootfs to include util-linux in the runtime closure.

Suggested reviewers: msample

🚥 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 clearly summarizes the main change: adding e2fsprogs and util-linux to microvm-rootfs and staging /var/lib/minimal.
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.
✨ 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 microvm-rootfs-writable-volume-support

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

…ed libs

e2fsprogs and util-linux both ship a libblkid.so.1 and libuuid.so.1 with the
same soname; the staging composition resolved them to e2fsprogs's older,
unversioned forks. util-linux's libmount then loaded those and warned
"libblkid.so.1: no version information available" on every fstrim/mount/blkid.

Repoint both sonames at util-linux's versioned libs (an ABI superset —
e2fsprogs's own mke2fs/e2fsck link them fine, verified by running a real
mkfs.ext4 format) and drop the e2fsprogs forks. A guard fails the build if a
package bump renames the util-linux targets so this can't silently regress.

Verified in the rebuilt image: fstrim --help and mount -V emit zero stderr
lines, mkfs.ext4 -V still resolves. Net image size drops ~175 KB.

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

@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.

🧹 Nitpick comments (1)
packages/microvm-rootfs/build.sh (1)

50-69: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consider fixing the duplicate soname issue upstream in e2fsprogs's build, instead of patching it here.

The comment correctly diagnoses the root cause: both e2fsprogs and util-linux ship libblkid/libuuid with the same soname. The conventional fix for this well-known conflict is to disable e2fsprogs's own copies at build time (--disable-libblkid --disable-libuuid) so only util-linux's versions ever ship, per Linux From Scratch: e2fsprogs's configure flags "prevent building and installing the libuuid and libblkid libraries... as Util-Linux installs more recent versions." Fixing this in packages/e2fsprogs/build.ncl/build.sh would eliminate the duplication at the source instead of every downstream consumer needing to repoint sonames and prune forks like this.

That said, this workaround is well-guarded (fails loudly if the util-linux filenames change) and the hardcoded versions (libblkid.so.1.1.0, libuuid.so.1.3.0) do match current util-linux upstream releases, so this is not a blocker.

One minor asymmetry: the rm -f at Line 66 removes the e2fsprogs fork files by hardcoded name with no existence guard (unlike the util-linux check above it). If e2fsprogs renames these in a future bump, rm -f silently no-ops rather than failing — the final ln -sf still repoints the soname correctly either way, but the stale e2fsprogs file would linger unpruned.

🤖 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 50 - 69, The cleanup step for
the old e2fsprogs libblkid/libuuid forks is too silent because the rm -f calls
can miss renamed files without surfacing a problem. Update the build.sh logic
around the ul_blkid and ul_uuid handling to verify the e2fsprogs fork filenames
before removing them, or otherwise fail loudly if they are absent, so the
repointing of libblkid.so.1 and libuuid.so.1 stays fully guarded against future
package renames.
🤖 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.

Nitpick comments:
In `@packages/microvm-rootfs/build.sh`:
- Around line 50-69: The cleanup step for the old e2fsprogs libblkid/libuuid
forks is too silent because the rm -f calls can miss renamed files without
surfacing a problem. Update the build.sh logic around the ul_blkid and ul_uuid
handling to verify the e2fsprogs fork filenames before removing them, or
otherwise fail loudly if they are absent, so the repointing of libblkid.so.1 and
libuuid.so.1 stays fully guarded against future package renames.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2284545f-3816-461b-ad3c-3fca9bf9f054

📥 Commits

Reviewing files that changed from the base of the PR and between 38409f1 and f2cf30b.

📒 Files selected for processing (1)
  • packages/microvm-rootfs/build.sh

…le-volume-support

# Conflicts:
#	packages/microvm-rootfs/build.ncl
#	packages/microvm-rootfs/build.sh
@norrietaylor

Copy link
Copy Markdown
Member Author

Re CodeRabbit's nitpick (fix the duplicate-soname issue upstream in e2fsprogs, and the unguarded rm -f): both are handled by the stacked follow-up #366, which configures e2fsprogs --disable-libblkid --disable-libuuid so only util-linux's libs ever ship, and removes this entire canonicalization block from microvm-rootfs/build.sh. Since #366 deletes the block, I haven't added a rm -f existence-guard here — it would be throwaway. #366 should be merged right after this.

@bryan-minimal bryan-minimal 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.

LGTM

@norrietaylor
norrietaylor added this pull request to the merge queue Jul 8, 2026
Merged via the queue into main with commit 04afaa7 Jul 8, 2026
6 checks passed
@norrietaylor
norrietaylor deleted the microvm-rootfs-writable-volume-support branch July 8, 2026 04:11
norrietaylor added a commit to gominimal/minimal that referenced this pull request Jul 9, 2026
* feat(minvmd): per-VM writable /dev/vdb — attach, provision, sync tunables

Host side of the per-VM writable ext4 volume (spec #583 Unit 1). Adds the
krun_add_disk3 FFI + SyncMode tri-state (KRUN_SYNC_{NONE,RELAXED,FULL}, a u32
not a bool) and Context::add_disk_with_sync. volume.rs provisions a sparse raw
image (ftruncate, allocate-on-write) at the resolved path — MINVMD_DATA_VOLUME_PATH
override, else <state>/data-vol.raw honouring XDG_STATE_HOME — and is idempotent.
vmm_child attaches /dev/vdb on every boot with the MINVMD_DISK_SYNC /
MINVMD_DISK_DIRECT_IO tunables (default relaxed / false).

Refs: #583

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

* feat(minimald): mount + relocate cache/state onto /dev/vdb

The guest formats /dev/vdb on first boot (ext4 superblock-gated mkfs, sized
1 MiB below the device to survive libkrun's backing-file trailer shave) and
mounts it read-write at /var/lib/minimal; cache + state then relocate onto it,
resolving the EXDEV hardlink constraint and persisting session state across
clean restarts. Mount presence uses try_exists so a stat error is not silently
treated as "no volume".

Refs: #583

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

* build: pin pkgs rootfs shipping /var/lib/minimal + e2fsprogs

microvm-rootfs now ships the /var/lib/minimal mountpoint and e2fsprogs +
util-linux (gominimal/pkgs#365/#366) so the guest can mkfs.ext4 and mount
/dev/vdb (and fstrim-reclaim it). Re-pin locked_commit to 12f324d8.

Refs: #583

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

* ci(minvmd,minimald): assert libkrun >= 1.19 in the VM boot jobs

krun_add_disk3 (this PR's /dev/vdb attach) needs libkrun >= 1.19.0. The macOS
"Verify libkrun" steps (boot-e2e / autospawn-e2e / build-macos) and the
Linux/KVM job now assert the libkrun in use exports krun_add_disk3, failing with
an actionable "upgrade the runner" message instead of a cryptic dyld
symbol-not-found at VM boot.

Refs: #583

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

* fix(minimald): format the data volume without a background lazy-init storm

On first boot the guest mkfs.ext4's the (default 32 GiB) /dev/vdb. With ext4's
default lazy inode-table init, the ext4lazyinit kernel thread zeroes ~512 MiB of
inode tables in the background right after mount — sustained I/O that lands
exactly when the guest is bringing up its vsock bridge + SSH server on 2 vCPUs.
That starved the first host->guest connect: a gvproxy-forwarder control request
timed out (5s) and `minimal ls` failed with `ssh connect: Disconnected`
(autospawn-e2e). boot-e2e was unaffected because its check used a 2 GiB volume.

Format with `-E lazy_itable_init=0,lazy_journal_init=0` (do the zeroing now, at
mkfs, not in the background) and `-i 65536` (~524K inodes / ~128 MiB table
instead of ~2M / ~512 MiB) so the synchronous init is small and one-time; later
boots detect the superblock and skip mkfs entirely.

Refs: #583

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

* fix(minimald): reject undersized data volumes before mkfs

run_mkfs_ext4 derived the ext4 block count as
`device_size_bytes.saturating_sub(MKFS_MARGIN_BYTES) / EXT4_BLOCK_BYTES`, which
is 0 for any device <= the 1 MiB margin — handing mkfs.ext4 a nonsensical size
argument. Reject a device below MKFS_MIN_DEVICE_BYTES (16 MiB, comfortably above
ext4's journal minimum) with InvalidInput before invoking mkfs. Only guards a
misconfigured MINVMD_VOLUME_BYTES or malformed device; the real volume is
GiB-scale.

Refs: #583

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

* fix(minimald): don't block SSH accept on the gvproxy egress-proxy publish

Server::run awaited start_host_proxies before entering the accept loop, and the
DM1 host-expose there (expose_proxy_on_host -> gvproxy /services/forwarder/expose)
blocks up to a 5s control-request timeout when the host gvproxy is slow. In that
window the SSH listener is bound but not accepting, so the first host->guest
connect lands in the gap and fails with `ssh connect: Disconnected` — seen in
autospawn-e2e, where `minimal ls` connects right after READY. Attaching the
per-VM data volume made the host gvproxy slow enough to hit this on macOS/HVF,
exposing the latent bug (main passes only because gvproxy responds fast).

The expose is already best-effort (warns + continues on failure), so detach it:
bind the proxy listeners synchronously as before, but spawn the host-expose so it
never gates the SSH accept loop.

Refs: #583

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

* revert(minimald): restore awaited gvproxy egress-proxy publish

This reverts commit 671bec4.

Detaching the host-side expose removed an accidental serialization
that main relies on. With the expose spawned, the first host->guest
SSH connect overlaps the guest->host expose control request on
libkrun's single vsock device and wedges it (#588), so cold
`minimal ls` fails with `ssh connect: Disconnected` in autospawn-e2e.
Awaiting the expose (as on main) serializes vsock usage so the accept
loop's first connection stays clean. The 5s cold-boot delay when the
host gvproxy is absent is pre-existing on main and out of scope here.

Refs: #588

* fix(minvmd): warn on unrecognized disk-tunable env values

resolve_disk_flags silently swallowed unrecognized MINVMD_DISK_SYNC and
MINVMD_DISK_DIRECT_IO values, falling back to the defaults with no signal.
Emit a tracing::warn! naming the env var and value when a set value is
unrecognized, keeping the existing relaxed/false defaults.

* fix(minvmd): treat concurrent data-volume creation as idempotent

ensure_sparse_raw probes metadata then create_new; a provisioner racing
between the two surfaced AlreadyExists as VolumeError::Create, breaking
the documented idempotent contract. Treat an AlreadyExists creation race
as success (the volume now exists) and keep the image as-is.

* ci: cover crates/minimald in the macOS lane

ci-macos boots minimald as pid-1 in the boot/e2e VM, yet its paths filter
omitted crates/minimald — so guest-daemon-only changes skipped every macOS
VM check, including autospawn-e2e. Add crates/minimald/** to the push +
pull_request filters so guest-daemon changes get the coverage that
exercises them.

* ci: provision a host gvproxy for the macOS e2e job

The Auto-spawn E2E boots the guest, which publishes its host-side proxy via
the host gvproxy over the vsock shuttle. With no gvproxy the publish blocks
until its 5s control-request timeout, holding Server::run's SSH accept loop
closed; the cold `minimal ls` connect-retry deadline then expires and the
list fails (`ssh connect: Disconnected`). This is a pre-existing ~40ms race
that /dev/vdb's ~60ms first-boot mkfs tips red. Fetch gvproxy and export
MINVMD_GVPROXY_BIN so the publish completes immediately, matching a real host.

* refactor(minimald): gate the data-volume mount on a ListenArgs field

Replace the second `is_minimal_microvm()` branch (per Tom's review) with a
hidden `mk_mount_state_volume: Option<String>` field on ListenArgs, set once
in the microVM config block. The mount is then driven by `if let Some(dev) =
listen_args.mk_mount_state_volume`. State/cache relocation stays gated on the
mount succeeding — pointing state at an unmounted /var/lib/minimal would land
it on the read-only rootfs (that becomes eager once Unit 2 makes mount
failure loud).

* docs(minimald): frame the mkfs options as consequences, not tuning

Per Tom's review: make explicit that the non-default mkfs.ext4 options are
not independent tuning but fall out of two deliberate choices — eager
inode/journal init (whose entailment is the reduced inode count) and
surviving libkrun's backing-file trailer shave (whose entailment is the
explicit block count + pinned block size). ext4's defaults are otherwise
left alone.

* refactor(minvmd): resolve data-volume path via paths::minimal_state_dir

Per Tom's review, source the state-dir base from the shared
`paths::minimal_state_dir()` resolver instead of the minvmd-local
`StateDir::default_path`. Keep the explicit `XDG_STATE_HOME` override first:
`dirs` (and thus `minimal_state_dir`) ignores it on macOS, and the e2e tests
isolate state via `XDG_STATE_HOME` — without it the volume would escape the
tests' temp dir onto the real state dir. Promote `paths` from a dev- to a
regular dependency.

* feat(minvmd): default the data volume to 256 GiB

Resolves Tom's review point: the image is sparse and offline resize is
hard, so size it big up front. Measured first-boot cost is flat in the
volume size — mkfs.ext4 writes only ~5-8 MiB of metadata regardless
(sparse backing + reduced inode ratio), so format+mount stays ~40 ms at
32/64/128/256 GiB on an M-series host. No boot-time penalty, so the default
goes to 256 GiB; MINVMD_VOLUME_BYTES still overrides.

Refs: #583

* refactor(minimald): drop the redundant mkfs -i override

Measurement (32/64/128/256 GiB) shows mke2fs already defaults to a ~65536
bytes/inode ratio at these volume sizes — `-i 65536` produces an identical
inode count (32 GiB -> 524288, 256 GiB -> 4194304, both 65536 B/inode). And
the eager inode-table init is cheap because the volume is sparse (zero-writes
land in holes; ~sub-ms at every size), not because of the inode count. So the
override earns nothing: drop it plus MKFS_BYTES_PER_INODE and correct the doc
rationale. Per Tom's review.

Refs: #583

* style(minvmd): rustfmt the path resolver + correct the flat-cost note

The wrapped closure exceeded rustfmt width (fmt CI). Also drop the stale
"reduced inode ratio" credit from the DEFAULT_VOLUME_BYTES note now that the
-i override is gone — the flat first-boot cost is purely the sparse backing.

* fix(minimald): cap the boot-path gvproxy expose so it can't stall SSH accept

start_host_proxies awaits expose_proxy_on_host on Server::run's boot path,
before the SSH accept loop serves. The `/services/forwarder/expose` control
request runs to the full 5s GVPROXY_CONTROL_TIMEOUT even with a host gvproxy
present (the forwarder control path isn't reachable over the shuttle in every
deployment), holding the accept loop closed. The cold `minimal ls`
connect-retry deadline (~5s) then expires as the loop would resume, so the
first list fails with `ssh connect: Disconnected` — the volume's ~60ms
first-boot mkfs tips a pre-existing race red on the e2e autospawn gate.

Cap the publish at HOST_EXPOSE_PUBLISH_TIMEOUT (1s): a best-effort publish
must never stall the SSH bridge. Best-effort semantics unchanged.

Refs: #588

* ci: drop the ineffective gvproxy provisioning from the e2e job

Provisioning a host gvproxy did not fix the autospawn stall — the boot log
shows the `/services/forwarder/expose` control request still times out at 5s
even with gvproxy present. The bounded expose (HOST_EXPOSE_PUBLISH_TIMEOUT)
fixes it in the daemon instead, so the fetch step + MINVMD_GVPROXY_BIN export
are dead weight; remove them.

* ci: reap the Session-E2E VM before the auto-spawn step

With boot-e2e and autospawn-e2e merged into one `e2e` job (upstream), the
session harness's leftover `__krun-vmm` (its Drop kills minvmd but not the
detached VMM grandchild) lingers into Auto-spawn. As a second concurrent VM
it wedges the host->guest bridge over libkrun's vsock (#588): with the expose
now bounded, the accept loop is up early yet the boot log shows no connection
ever accepted. Reap the VMM so Auto-spawn runs single-VM, as it did when it
was a standalone job.

The proper fix is the session harness reaping its own grandchild (process
group); tracked separately.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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