Source version analyzed: bootc v1.16.7
Tested operating system: Fedora 44 (aarch64, UEFI, GRUB)
Summary
A native-composefs installation with a separate boot filesystem mounts that
filesystem read-only at /boot, but bootc's composefs runtime code expects the
same filesystem at /sysroot/boot. Nothing mounts it there. As a result, bootc
reads the underlying physical root's stale installation-time /boot directory
instead of the real boot filesystem.
This causes bootc status and consumers that call it, including DNF, to fail
while looking for BLS entries. The same path is used by composefs upgrade,
rollback, finalization, deletion, and garbage-collection code.
Installation scenario
The system was installed using bootc install to-filesystem with the native
composefs backend, GRUB, a Btrfs root filesystem, a separate ext4 boot
filesystem, and a VFAT EFI System Partition. The relevant arguments included:
--composefs-backend
--bootloader=grub
--root-mount-spec=/dev/disk/by-label/root
--boot-mount-spec=UUID=<boot-filesystem-uuid>
After boot, the mount topology was:
$ findmnt /sysroot
TARGET SOURCE FSTYPE OPTIONS
/sysroot /dev/mapper/root[/root] btrfs ro,noatime,seclabel,compress=zstd:3,...
$ findmnt /boot
TARGET SOURCE FSTYPE OPTIONS
/boot /dev/nvme0n1p2 ext4 ro,relatime,seclabel
$ findmnt /sysroot/boot
# no result
$ findmnt /boot/efi
# no result
/sysroot/boot existed as an ordinary directory containing remnants from the
installation process, but it was not the separate boot filesystem.
Failure
bootc status failed while attempting to read BLS entries from the wrong
directory:
error: Status: Prepending custom prefix to EFI and BLS entries: Getting sorted Type1 boot entries: No such file or directory (os error 2)
This also prevented dnf update from proceeding because its bootc status check
returned the same error. SELinux audit messages printed around the command were
unrelated; this was a missing mount/path problem, not a labeling denial.
Source analysis
The install and runtime paths disagree:
- In crates/lib/src/install.rs, a non-empty --boot-mount-spec becomes a
MountSpec targeting /boot and is forced read-only.
- In crates/lib/src/bootc_composefs/boot.rs,
setup_composefs_bls_boot() converts that spec into a kernel argument of the
form systemd.mount-extra=<source>:/boot:<fstype>:<options>.
- In crates/lib/src/store/mod.rs, BootedStorage::new() handles a
composefs/GRUB system by opening boot relative to the physical root. On a
running system that physical root is /sysroot, so the resulting boot
directory is /sysroot/boot.
- Storage::bls_boot_binaries_dir() explicitly documents /sysroot/boot as
the GRUB Type 1 boot-binary location. The status and composefs lifecycle code
then reads loader/entries relative to that directory.
Remounting /sysroot read-write in bootc's private mount namespace does not
solve this. A filesystem mounted at /boot is not thereby also mounted at
/sysroot/boot.
The ESP is discovered and temporarily mounted by some bootc operations, but
the installed system also lacked the conventional persistent /boot/efi
mount. That leaves bootloader tooling outside those narrowly scoped bootc
operations without the expected ESP path.
Demonstrated workaround
Installing the following native systemd mount topology fixes the failure:
- Mount the boot filesystem by UUID read-write at /sysroot/boot.
- Bind-mount /sysroot/boot read-only at /boot.
- Mount the ESP by UUID read-write at /boot/efi.
In unit terms, the essential relationships are:
# sysroot-boot.mount
[Mount]
What=/dev/disk/by-uuid/<boot-filesystem-uuid>
Where=/sysroot/boot
Type=ext4
Options=rw
# boot.mount
[Mount]
What=/sysroot/boot
Where=/boot
Type=none
Options=bind,ro
# boot-efi.mount
[Mount]
What=/dev/disk/by-uuid/<esp-filesystem-uuid>
Where=/boot/efi
Type=vfat
Options=rw,fmask=0177,dmask=0077
With these units active, /sysroot/boot is the writable authoritative boot
filesystem expected by bootc, /boot remains protected from ordinary package
manager writes, and the ESP is available at its conventional path.
Expected behavior
bootc install to-filesystem --composefs-backend should leave a separate boot
filesystem in a runtime mount arrangement consistent with bootc's own
composefs storage API. At minimum, a successful installation using
--boot-mount-spec should allow bootc status, upgrade, rollback, staged
finalization, and garbage collection to access the actual BLS entries.
Possible fixes include:
- Have native-composefs installation generate the persistent systemd mount
topology above, including appropriate ordering and UUID references.
- Or change the composefs runtime implementation to operate consistently on
the existing /boot mount, remounting or cloning it privately when writes
are required, and audit every caller that currently assumes
/sysroot/boot.
A regression test should install native composefs with a separate boot
filesystem, boot it, verify both the public read-only and private physical boot
paths, and exercise bootc status plus a staged upgrade.
AI assistance: OpenAI Codex (GPT-5) helped analyze the observed mount state,
review the bootc v1.16.7 source, and draft this report.
Source version analyzed: bootc v1.16.7
Tested operating system: Fedora 44 (aarch64, UEFI, GRUB)
Summary
A native-composefs installation with a separate boot filesystem mounts that
filesystem read-only at /boot, but bootc's composefs runtime code expects the
same filesystem at /sysroot/boot. Nothing mounts it there. As a result, bootc
reads the underlying physical root's stale installation-time /boot directory
instead of the real boot filesystem.
This causes bootc status and consumers that call it, including DNF, to fail
while looking for BLS entries. The same path is used by composefs upgrade,
rollback, finalization, deletion, and garbage-collection code.
Installation scenario
The system was installed using bootc install to-filesystem with the native
composefs backend, GRUB, a Btrfs root filesystem, a separate ext4 boot
filesystem, and a VFAT EFI System Partition. The relevant arguments included:
After boot, the mount topology was:
/sysroot/boot existed as an ordinary directory containing remnants from the
installation process, but it was not the separate boot filesystem.
Failure
bootc status failed while attempting to read BLS entries from the wrong
directory:
This also prevented dnf update from proceeding because its bootc status check
returned the same error. SELinux audit messages printed around the command were
unrelated; this was a missing mount/path problem, not a labeling denial.
Source analysis
The install and runtime paths disagree:
MountSpec targeting /boot and is forced read-only.
setup_composefs_bls_boot() converts that spec into a kernel argument of the
form systemd.mount-extra=<source>:/boot:<fstype>:<options>.
composefs/GRUB system by opening boot relative to the physical root. On a
running system that physical root is /sysroot, so the resulting boot
directory is /sysroot/boot.
the GRUB Type 1 boot-binary location. The status and composefs lifecycle code
then reads loader/entries relative to that directory.
Remounting /sysroot read-write in bootc's private mount namespace does not
solve this. A filesystem mounted at /boot is not thereby also mounted at
/sysroot/boot.
The ESP is discovered and temporarily mounted by some bootc operations, but
the installed system also lacked the conventional persistent /boot/efi
mount. That leaves bootloader tooling outside those narrowly scoped bootc
operations without the expected ESP path.
Demonstrated workaround
Installing the following native systemd mount topology fixes the failure:
In unit terms, the essential relationships are:
With these units active, /sysroot/boot is the writable authoritative boot
filesystem expected by bootc, /boot remains protected from ordinary package
manager writes, and the ESP is available at its conventional path.
Expected behavior
bootc install to-filesystem --composefs-backend should leave a separate boot
filesystem in a runtime mount arrangement consistent with bootc's own
composefs storage API. At minimum, a successful installation using
--boot-mount-spec should allow bootc status, upgrade, rollback, staged
finalization, and garbage collection to access the actual BLS entries.
Possible fixes include:
topology above, including appropriate ordering and UUID references.
the existing /boot mount, remounting or cloning it privately when writes
are required, and audit every caller that currently assumes
/sysroot/boot.
A regression test should install native composefs with a separate boot
filesystem, boot it, verify both the public read-only and private physical boot
paths, and exercise bootc status plus a staged upgrade.
AI assistance: OpenAI Codex (GPT-5) helped analyze the observed mount state,
review the bootc v1.16.7 source, and draft this report.