composefs backend
Experimental features are subject to change or removal. Please do provide feedback on them.
Overview
The composefs backend is an experimental alternative storage backend that uses composefs-rs instead of ostree for storing and managing bootc system deployments.
Status: Experimental. The composefs backend is under active development and not yet suitable for production use. The feature is always compiled in as of bootc v1.10.1.
A key goal is custom "sealed" images, signed with your own Secure Boot keys. This is based on Unified Kernel Images that embed a digest of the target container root filesystem, typically alongside a bootloader (such as systemd-boot) also signed with your key.
How Sealed Images Work
A sealed image is a cryptographically signed and verified bootc image that provides end-to-end integrity protection. This is achieved through:
- Unified Kernel Images (UKIs): Combining kernel, initramfs, and boot parameters into a single signed binary
- Composefs integration: Using composefs with fsverity for content-addressed filesystem verification
- Secure Boot: Cryptographic signatures on both the UKI and systemd-boot loader
A sealed image includes:
- composefs digest: A SHA-512 hash of the entire root filesystem, computed at build time
- Unified Kernel Image (UKI): A single EFI binary containing the kernel, initramfs, and kernel command line with the composefs digest embedded
- Secure Boot signature: The UKI is signed with your private key
At boot time, the composefs digest in the kernel command line (e.g., composefs=<sha512-hash>) is verified against the mounted root filesystem. This creates a chain of trust from firmware to userspace, ensuring the system will only boot if the root filesystem matches exactly what was signed.
Building Sealed Images
Prerequisites
For sealed images, the container must:
- Include a kernel and initramfs in
/usr/lib/modules/<kver>/ - Have systemd-boot available (and NOT have
bootupd) - Not include a pre-built UKI (the build process generates one)
Sealed images also require:
- Secure Boot support in the target system firmware
- A filesystem with fsverity support (e.g., ext4, btrfs) for the root partition
Using without Secure Boot
You can use a sealed UKI without Secure Boot enabled. The composefs and mounting code is fully orthogonal to Secure Boot - the fsverity digest of the root filesystem and all of its contents will still be validated at runtime, which does provide an increased level of integrity.
However: nothing validates that root digest itself, meaning any locally running code can replace the UKI (e.g. after a container breakout) and fully control the next boot.
It is intentional to support booting with Secure Boot disabled, because a valid use case is to temporarily disable it in order to test a change locally on e.g. one machine, then re-enable it later. However at the current time it is not yet streamlined to regenerate the UKI locally.
Build Pattern: Compute Digest and Generate UKI in One Stage
The key to building sealed images is using a multi-stage Dockerfile where a separate stage mounts the target rootfs, computes its composefs digest, and generates the signed UKI in one step:
# Build your rootfs with all packages and configuration
FROM <base-image> as rootfs
RUN apt|dnf|zypper install ... && bootc container lint --fatal-warnings
# Generate the sealed UKI in a tools stage
FROM <tools-image> as sealed-uki
RUN --mount=type=bind,from=rootfs,target=/target \
--mount=type=secret,id=secureboot_key \
--mount=type=secret,id=secureboot_cert <<EORUN
set -euo pipefail
# Compute the composefs digest from the mounted rootfs
digest=$(bootc container compute-composefs-digest /target)
# Find the kernel version
kver=$(ls /target/usr/lib/modules)
# Generate and sign the UKI with the digest embedded
ukify build \
--linux "/target/usr/lib/modules/${kver}/vmlinuz" \
--initrd "/target/usr/lib/modules/${kver}/initramfs.img" \
--cmdline "composefs=${digest} rw" \
--os-release "@/target/usr/lib/os-release" \
--signtool sbsign \
--secureboot-private-key /run/secrets/secureboot_key \
--secureboot-certificate /run/secrets/secureboot_cert \
--output "/out/${kver}.efi"
EORUN
# Final image: copy the sealed UKI into place
FROM rootfs
COPY --from=sealed-uki /out/*.efi /boot/EFI/Linux/
This pattern works because:
- The
--mount=type=bind,from=rootfsprovides read-only access to the target filesystem bootc container compute-composefs-digestcomputes the SHA-512 hash of the rootfsukifycreates the UKI with that digest in the kernel command line (composefs=<digest>)- The final stage copies the signed UKI into the rootfs without modifying any files used in the digest calculation
The bootc container compute-composefs-digest Command
bootc container compute-composefs-digest [PATH]
Computes the composefs digest for a filesystem. The digest is a 128-character SHA-512 hex string that uniquely identifies the filesystem contents.
Options:
PATH: Path to the filesystem root (default:/target)--write-dumpfile-to <PATH>: Generate a dumpfile for debugging
Note: This command is currently hidden from
--helpoutput as it's part of the experimental composefs feature set.
Final Image Structure
The sealed image should have:
- The signed UKI at
/boot/EFI/Linux/<kver>.efi - A signed systemd-boot at
/boot/EFI/BOOT/BOOTX64.EFIand/boot/EFI/systemd/systemd-bootx64.efi - The raw
vmlinuzandinitramfs.imgremoved from/usr/lib/modules/<kver>/(they're now embedded in the UKI)
External Signing Workflow
For production environments with dedicated signing infrastructure:
- Build unsigned UKI: Compute digest and create an unsigned UKI (omit
--signtoolfrom ukify) - Sign externally: Take the unsigned UKI to your signing infrastructure
- Complete the seal: Inject the signed UKI into the final image
This workflow is planned for streamlining in future releases (see #1498).
Developing and Testing bootc with composefs
See CONTRIBUTING.md for information on building and testing bootc itself with composefs support.
Bootloader Support
To use sealed images, the container image must have a UKI and systemd-boot installed (and not have bootupd). If these conditions are met, bootc will automatically detect and use the composefs backend during installation.
Installation
There is a --composefs-backend option for bootc install to explicitly select a composefs backend apart from sealed images; this is not as heavily tested yet.
Known Issues
The composefs backend is experimental; on-disk formats are subject to change.
Deployment blockers
- Garbage collection: In progress
- Extended install APIs: Ability to cleanly implement anaconda %post and osbuild post mutations and general post-install pre-reboot; right now some tools just mount the deployment directory (note this one also relates to APIs in general)
- OCI registry install: Installing from registry can fail due to config mismatch (suggestion: just clean reject v2s2)
- composefs-rs repository finalization
Important
- Extended test suite: Right now we're not covering upgrades well, need to build upgrade image in sealed cases
- Full workflow test - add composefs into https://gitlab.com/fedora/bootc/tests/bootc-workflow-test for example
- Workflow upgrades especially "from old systems"
- Unified storage: Not strictly a blocker but a really nice to have
- Sealed image build UX: Streamlined tooling for building sealed images
- In place transitions:
- First: support factory reset from ostree to composefs
- Next: Support copying /etc and /var
- A lot more practical level docs for using this
Minor
- Remove
/usr/lib/bootc/kargs.das part of UKI creation (alsobootc container inspectshould show UKI kargs)
Additional Resources
- See filesystem.md for information about composefs in the standard ostree backend
- See bootloaders.md for bootloader configuration details
- composefs-rs - The underlying composefs implementation
- Unified Kernel Images specification
- ukify documentation - Tool for building UKIs
The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see Trademark Usage.