bug(vm-driver): registry image prep expects bundle-style rootfs/ from `umoci raw unpack` — failure masked as success and broken disk cached forever
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 8.7k
- Forks
- 1.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 253
Description
Agent Diagnostic
- Investigated with a coding agent against a live macOS (Apple silicon) gateway running the VM driver, plus the repo source at current
main. - Tested OpenShell v0.0.86 (latest release at time of filing;
main'scrates/openshell-driver-vm/scripts/openshell-vm-sandbox-init.shcontains the same defect, so the bug reproduces on latest). - Searched existing issues: no duplicate. #2149 is related only in symptom — the exit-code masking described below collapses many distinct guest failures into the same vague "VM process exited with status 0".
- Investigation path: sandbox conditions → gateway logs → per-sandbox
rootfs-console.log→ prepared-disk inspection withdebugfs/e2fsck→ captured the (deleted-on-exit) image-prep VM console by snapshotting the staging dir during prep → finally booted the prep VM manually withopenshell-driver-vm --internal-run-vmagainst a repaired copy of the poisoned disk, which surfaced the real guest error. - The agent did root-cause and fix this locally (fix below); this issue is filed so the fix can land upstream.
Description
On a VM-driver gateway with no local container engine visible, creating a sandbox from any registry image other than the bootstrap/base image permanently fails with:
Ready=False reason=ProcessExited message="VM process exited with status 0"
and the sandbox's rootfs-console.log shows:
[0.074s] FATAL: prepared image disk missing /image-rootfs
Three stacked defects cause this:
-
Root cause — wrong expectation of
umoci raw unpackoutput. Inscripts/openshell-vm-sandbox-init.sh,prepare_guest_image_rootfs()runs:/opt/openshell/bin/umoci raw unpack --image "$payload_dir/oci:openshell" "$partial_root" if [ ! -d "$partial_root/rootfs" ]; then ts "FATAL: umoci unpack did not produce rootfs directory" exit 1 fi mv "$partial_root/rootfs" "$image_root"umoci raw unpackextracts the image filesystem directly into the target directory — there is no bundle-stylerootfs/subdirectory (that'sumoci unpack). The unpack actually succeeds every time; manually booting the prep VM shows:• unpacking rootfs ... • ... done • unpacked image rootfs: /overlay/image-rootfs.partial FATAL: umoci unpack did not produce rootfs directoryand
debugfs -R "ls /image-rootfs.partial"on the disk shows a complete rootfs (bin boot dev etc home lib ...) directly underimage-rootfs.partial/. The script then FATALs and exits despite a successful unpack. -
Masking bug A — guest init exit codes do not survive the libkrun boundary. When guest PID 1 exits (any status), the microVM powers off and the launcher's worker process exits 0.
run_image_prep_vmincrates/openshell-driver-vm/src/driver.rsjudges prep success purely by that process status, so the failed prep looks successful. (Same masking turns the sandbox-boot FATAL into the vague "VM process exited with status 0" — see also #2149, a different bug with the identical reported symptom.) -
Masking bug B — the prepared disk is cached without validation.
build_prepared_image_diskrenames the disk into the content-addressed cache without checking it contains/image-rootfs. The broken disk (payload only, no unpacked rootfs, and unclean-shutdown bitmap-checksum damage since init exits withoutsync/umount) then poisons every subsequent sandbox for that image digest until the cache directory is deleted by hand.
Why this is rarely seen: with Docker/Podman visible, prep uses the local-docker tar export path; the base image needs no prep at all (it is the bootstrap rootfs). Only the registry/umoci fallback path is affected.
Reproduction Steps
- Run a gateway with the VM driver on a host where the gateway process cannot see a container engine (
vm driver: no local container engine available, falling back to registryappears in the log). openshell sandbox create --image ghcr.io/nvidia/openshell-community/sandboxes/pi:latest --name pi-test(any non-base registry image reproduces).- Sandbox reaches
errorwithProcessExited: VM process exited with status 0; the per-sandboxrootfs-console.logunder the VM driver state dir showsFATAL: prepared image disk missing /image-rootfs. - Deleting and recreating never recovers (poisoned cache at
<state-dir>/images/sandbox-prepared-rootfs-ext4-umoci-v3-*); deleting that directory rebuilds the disk, which breaks identically. - To see the true failure: copy the cached
rootfs.ext4,e2fsck -fyit, then boot it manually withopenshell-driver-vm --internal-run-vm --vm-root-disk <bootstrap rootfs.ext4> --vm-overlay-disk <copy> --vm-exec /srv/openshell-vm-sandbox-init.sh --vm-env OPENSHELL_VM_INIT_MODE=image-prep --vm-console-output <log>and read the console.
Environment
- OS: macOS 15 (Apple silicon), libkrun backend
- Container engine: none visible to the gateway process (registry/umoci prep path)
- OpenShell: v0.0.86 (Homebrew); defect also present in
main'sscripts/openshell-vm-sandbox-init.sh - Latest release checked: yes (v0.0.86 is latest)
- Possible duplicates checked: yes — #2149 shares the symptom string but is a different failure (BYOC read-only
/image-cachemount); no issue matches this root cause
Logs
# gateway log (prep looks successful; no error between these lines)
WARN openshell_driver_vm::driver: vm driver: no local container engine available, falling back to registry image_ref=ghcr.io/nvidia/openshell-community/sandboxes/pi:latest
INFO openshell_driver_vm::driver: vm driver: sandbox root disk plan resolved ... image_identity=sandbox-prepared-rootfs-ext4-umoci-v3:openshell-0.0.86:sha256:00d0c5e9...
# sandbox rootfs-console.log (every boot from the poisoned cache)
[0.002s] setting up writable overlay root
[0.074s] FATAL: prepared image disk missing /image-rootfs
# prep VM console, captured via manual --internal-run-vm boot (the real error)
[0.003s] setting up writable overlay root
[0.055s] preparing sandbox image rootfs in guest (oci-layout)
• unpacking rootfs ...
• ... done
• unpacked image rootfs: /overlay/image-rootfs.partial
[10.598s] FATAL: umoci unpack did not produce rootfs directory
# the "missing" rootfs, present all along (after e2fsck repairs the unclean state)
$ debugfs -R "ls /image-rootfs.partial" rootfs-copy.ext4
bin boot dev etc home lib media mnt opt proc root ...
Fix
Verified working locally (a pi-image sandbox reaches ready and runs /usr/bin/pi):
scripts/openshell-vm-sandbox-init.sh: afterumoci raw unpack, move$partial_rootitself to$image_root(keeping a defensive branch for a bundle-stylerootfs/layout).driver.rs::build_prepared_image_disk: validate the produced disk contains/image-rootfs(read-onlydebugfs -R "stat /image-rootfs") before renaming it into the cache; on failure, do not cache and include the prep VM console tail in the error instead of reporting success.driver.rs::prepared_image_disk_size_bytes: size for payload + unpacked rootfs coexisting (payload*4 + 1 GiBinstead ofpayload*3 + 512 MiB; the file is sparse, so headroom is free) — with the layout bug fixed, the old floor is a near-miss for ~1.2 GB-compressed images.
Happy to open a PR with these changes.
Agent-First Checklist
- I pointed my agent at the repo and had it investigate this issue
- I loaded relevant skills (e.g.,
debug-openshell-cluster,debug-inference,openshell-cli) - I checked the latest OpenShell release and either reproduced the issue there or explained why I cannot upgrade/test it
- I searched existing issues for possible duplicates or explained why I could not
- My agent could not resolve this — the diagnostic above explains why (resolved locally; filed so the fix lands upstream)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with crates/openshell-driver-vm/scripts/openshell-vm-sandbox-init.sh and driver.rs functions run_image_prep_vm, build_prepared_image_disk, and prepared_image_disk_size_bytes. Reproduce the registry/umoci path or inspect the provided prep VM console and cached disk behavior. Done means a non-base registry image reaches ready, prep failures are reported, invalid disks are not cached, and sufficient disk headroom is used.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, shell
- Domain
- infrastructure
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100