Repository navigation
Conversation
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2b465b6. Configure here.
2b465b6 to
fb40cc1
Compare
5675147 to
6bde47c
Compare
6bde47c to
abdceaf
Compare
abdceaf to
0d9f270
Compare
0d9f270 to
de83eaa
Compare
2093a74 to
92dd458
Compare
92dd458 to
5ec53e4
Compare
5ec53e4 to
e290067
Compare
e290067 to
c69e90b
Compare
d4801d7 to
f24fed3
Compare
cb42b2d to
3a95ff6
Compare
3a95ff6 to
3f9ef7f
Compare
4500276 to
a5e47e3
Compare
a5e47e3 to
c323a22
Compare
c323a22 to
fb28360
Compare
fb28360 to
e48db8d
Compare
e48db8d to
0d729b3
Compare
0d729b3 to
dac88a2
Compare
41aad4a to
f9d76cf
Compare
composeRootfs merges an image's layers in manifest order, applying each layer blob from the shared OCI cache with umoci's DirRootfs format so whiteouts, opaque directories, file/directory replacement and hardlinks behave as the tar stream specifies. The merged tree is exported to the single read-only disk the guest mounts, which is why layers are shared as blobs and artifacts rather than mounted per layer: every hypervisor keeps its existing vda/vdb contract. The umoci unpack path this replaces is removed with its private helpers. Manifest validation, the pull-failure message and the layer_unpack build-phase metric label are preserved so existing dashboards and alerts keep working.
sjmiller609
left a comment
There was a problem hiding this comment.
- If we export a full disk per image, I think that still has the issue of duplicating storage for the case of many-VMs with mostly-the-same images or all shared base. So I think that means it doesn't have a significant benefit compared to the previous way where we just have the rootfs created for each VM. I think this would have to do something where we are sharing these blobs between different VMs without duplicating them somehow, in order to get the benefit
- doing that could potential have disk IO or other performance consequences, so I'm curious about doing benchmarking when we are swapping out the rootfs, maybe try this skill
/benchmark-hypeman-forkbut also try just plain fresh create, or in parallel from partially shared blobs maybe
Let me know if I'm missing something here
so looking through #458 made me realize that PR doesn't add de-duplication so I need to rework that PR, but this PR is necessary for per-layer de-duplication that will be added in the follow-up PR. will benchmark resource utilization before re-requesting update: benchmarking shows that this PR shouldn't be a regression for performance. |
sjmiller609
left a comment
There was a problem hiding this comment.
We’re changing the production build path for the rootfs in this PR, but it seems like a subsequent PR would need to change it again, but we haven't worked out how that will happen yet so I think we shouldn't merge this in yet (because we would have to change how the rootfs works twice to get to the end goal of storage deduplication). I think that exporting separate disks containing overlapping layer data is the behavior we need to avoid to avoid duplication.
Looking forward into the next PR, it looks like the approach is solving for identical-prefix sharing, but not for general layer de-duplication.
#458 still merges all layers except the last into one exported base disk. your concern applies: that disk is shared only when the entire ordered base-layer prefix matches.
for example:
image A: [ubuntu, dependencies-A, app-A]
image B: [ubuntu, dependencies-B, app-B]
the exported bases are [ubuntu, dependencies-A] and [ubuntu, dependencies-B]. both contain their own copy of ubuntu, despite sharing that input layer.
In terms of how it should work, I think we would need to be extending the existing OverlayFS setup in guest init to mount multiple shared read-only layers. Here's what hypeship says:
the established pattern is immutable shared layers plus a private writable overlay—not a separately flattened copy of every image.
the straightforward design
convert each OCI layer once into a content-addressed, read-only EROFS artifact.
expose the required artifacts to each VM without copying them.
inside the guest, mount those layers and use OverlayFS to present their ordered contents as one root filesystem.
give each VM its own writable upper layer. image layers—including the final application layer—stay shared.
for your example, both VMs reference the same ubuntu artifact, even though their dependencies differ. whiteouts and overrides determine what each guest sees; they don’t require copying the base.this is the architecture used by containerd’s EROFS snapshotter. adopting it in hypeman still requires validating device limits, guest support, snapshot/restore, and performance across hypervisors.
We need to measure disk I/O, fanout, cold boot, and physical storage before committing to the new direction.
|
closing as superseded. the next implementation will target content-addressed artifacts for every OCI layer and compose them as multiple read-only OverlayFS lower layers, rather than introducing an intermediate flattened rootfs production path. benchmark coverage will be added before enabling the new runtime path. |

summary
replace the old OCI rootfs unpack path with explicit layer-by-layer composition from the shared OCI cache.
this keeps the guest and hypervisor contract unchanged: hypeman still produces one read-only rootfs disk for the VM.
pre-existing flow
The old path asked umoci to resolve the manifest and unpack the complete image directly from the OCI layout:
Pseudocode:
new flow
The new path reads the persisted manifest model and applies each layer blob explicitly, in manifest order:
Pseudocode:
what changed
composeRootfs, which applies shared layer blobs with umoci'sDirRootfsformat.layer_unpackmetric label and existing pull-failure message shape for observability compatibility.unpackLayersimplementation and its manifest conversion helpers.why one disk remains
The guest currently mounts one read-only lower disk (
/dev/vda) and one writable upper disk (/dev/vdb). That contract is shared by Cloud Hypervisor, Firecracker, QEMU, and vz, so this PR composes the layers into one rootfs rather than attaching one device per layer.Layer blobs are shared in the host-side OCI cache; the VM still receives its existing single rootfs disk. Layer-artifact reuse is a separate follow-up path.
failure and cleanup behavior
Composition happens in an isolated
.compose-*directory. A successful compose replaces the destination tree. A failure or crash can leave the destination absent or strand a staging directory, so GC/reconciliation must remove stale staging trees in a follow-up.Callers currently use fresh temporary export directories and do not read the destination concurrently.
validation
fsck.erofs --extract.main.