Skip to content

Native composefs with a separate boot filesystem does not make it available at /sysroot/boot #2399

Description

@jmpolom

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:

  1. Mount the boot filesystem by UUID read-write at /sysroot/boot.
  2. Bind-mount /sysroot/boot read-only at /boot.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions