Skip to content

lints: Don't require ostree bits on composefs-native images - #2500

Merged
cgwalters merged 8 commits into
bootc-dev:mainfrom
cgwalters-forge:bot/lint-ostree-symlink-composefs
Oct 2, 2026
Merged

cgwalters merged 8 commits into
bootc-dev:mainfrom
cgwalters-forge:bot/lint-ostree-symlink-composefs

Conversation

@cgwalters-bot

@cgwalters-bot cgwalters-bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

bootc container lint requires an /ostree -> sysroot/ostree symlink (the
fatal baseimage-root lint) and warns unless ostree's prepare-root.conf
enables composefs (baseimage-composefs). Both are meaningless for images
that are only ever deployed with the composefs backend, and force them to
carry ostree cruft. Of the options discussed in #2256, detecting
/usr/lib/composefs/setup-root-conf.toml (even if empty) is the one that is
actionable now, and it mirrors how prepare-root.conf signals ostree.

So when that file exists, a missing /ostree is accepted and the
prepare-root.conf check is skipped. If /ostree is present anyway it is
still validated, and /sysroot is still required since both backends mount
the physical root there. The lint descriptions, bootc-images.md (which
called the /ostree requirement a bug) and bootc-setup-root-conf.toml(5)
now describe the file as the composefs-native marker.

Testing: extended the baseimage-root unit test to cover a composefs-native
image without /ostree, with a bogus /ostree directory, and without
/sysroot, and the baseimage-composefs test to cover a composefs-native
image whose prepare-root.conf disables composefs. On a 16-core RHEL 10
devspace, rebased on current main: cargo test -p bootc-lib (271
passed, the same count as main since existing tests were extended) and just validate passed.

CI note (2026-09-24): the test-integration (fedora-44, composefs, ext4, grub, bls, unsealed) failure is unrelated to this change: the container test install config failed with Loading configuration: No such file or directory, a race with the concurrently running printconfig --all test, which creates and deletes /run/bootc/install/10-test.toml while print-configuration scans that directory. The same leg passed on other PRs with the same base. Failed jobs will be rerun once the run completes.

bootc requires DCO: the commits have no Signed-off-by, so a maintainer must sign off before merging (e.g. git rebase --signoff <base> and force-push).

Per review, bootc install now uses the same marker (folded in from cgwalters-forge#23): an image with setup-root-conf.toml and no ostree prepare-root.conf is installed with the composefs backend without --composefs-backend, and bootc-installation(7) states that the image determines the backend. The composefs CI images are now built that way (marker shipped, prepare-root.conf removed, bootloader set in an install config), and no composefs CI path passes --composefs-backend except test-upgrade, which installs the published base image. Tested on a 16-core devspace with BOOTC_base=quay.io/fedora/fedora-bootc:44: just validate and just unit-tests passed, and so did tmt readonly, 23, 32 and 52 on composefs grub BLS without the flag.

Closes: #2256

The Signed-off-by: Colin Walters <walters@verbum.org> on these commits was added on cgwalters's approval of the review draft: cgwalters-forge#4 (review)

Generated-by: https://github.com/cgwalters/#llms

@cgwalters cgwalters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI failure seems legit

@cgwalters-bot cgwalters-bot moved this to In Review in Composefs Stable Sep 25, 2026
auto-merge was automatically disabled September 25, 2026 20:53

Head branch was pushed to by a user without write access

@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

The failure was a real bug, but in the tests rather than in this change: printconfig --all writes and deletes /run/bootc/install/10-test.toml while install config runs bootc install print-configuration in parallel, and a drop-in that vanishes between the directory scan and the read fails with Loading configuration: No such file or directory. The same failure hit an unrelated PR branch (install-read-ssh-keys-before-mounts, centos-9 ostree) (job). On a devspace, 12 parallel loops of those two tests on this PR's image hit it 5 times in 18000 runs.

Fixed in a new commit on top, aec657d "tests-integration: Fix race between the install config tests", which merges the two into one test. Your lint commit is unchanged, but the new commit has no sign-off yet, so it needs your re-approval.

Tested on a 16-core devspace: the same stress loop on the fixed image had 0 failures in 18000 runs; just validate, just unit-tests, and for fedora-44 composefs ext4 systemd uki sealed, the container integration tests plus the tmt plans readonly and image-upgrade-reboot all passed.

Generated-by: https://github.com/cgwalters/#llms

@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

Rebased onto main; 2 commits, no content change.

Generated-by: https://github.com/cgwalters/#llms

@cgwalters-bot
cgwalters-bot force-pushed the bot/lint-ostree-symlink-composefs branch from aec657d to 95920b0 Compare September 26, 2026 03:45
cgwalters
cgwalters previously approved these changes Sep 28, 2026
@cgwalters-bot
cgwalters-bot force-pushed the bot/lint-ostree-symlink-composefs branch from 95920b0 to 379ba6c Compare September 28, 2026 14:52
@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

Rebased onto main; 2 commits, no content change.

Generated-by: https://github.com/cgwalters/#llms

@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

Rebased onto main 1968e52 to resolve conflicts with the docs file renames; no content changes (git range-diff 1968e52f c5421ac2 217b7c7f).

Generated-by: https://github.com/cgwalters/#llms

Comment thread crates/lib/src/lints.rs Outdated
Comment on lines +605 to +607
/// Whether the image is intended to be deployed only with the composefs
/// backend, which is signaled by the presence of a setup-root configuration
/// file (even if empty).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file might not even exist though as we don't really hard require it. I think the order to check if we have an image that's intended to be deployed with cfs backend only should be

  1. Check if we have UKI in the image
  2. Check if we only have systemd-boot or grub-cc as the bootloader
  3. Fallback to this

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file might not even exist though as we don't really hard require it.

Yes! But the point is that it serves as a nice declarative statement that composefs is desired.

Check if we have UKI in the image

Yes this is the status quo, and I think made sense at the time as it's clearly easy to find.

Check if we only have systemd-boot or grub-cc as the bootloader

But this is the debate: I think it's a lot cleaner to have "I want composefs" to be signaled by the presence of a composefs-related file that an image may want to include anyways (for transient /etc) versus scraping the bootloader state.

While we're never going to get away from inspecting bootloaders in general (we have inbound work to do so for Android Boot), it's IMO cleaner if we try to push towards expecting them to conform to a standardized interface.

(Also there's complications in looking at the bootloader if we want to make it a dynamic install-time choice, as you were looking at at one point right?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you're right about the bootloader bit. This file approach does seem to be the best

Comment thread docs/src/man/bootc-setup-root-conf.5.md Outdated
If the file does not exist all options take their documented defaults.

The presence of this file (even if empty) also marks the image as
composefs-native; `bootc container lint` then no longer requires the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually though what's missed here is this logic needs to be used at install time too, let's fix that in install.rs and also update the install docs to be clear, if you want composefs by default (and really only composefs), ensure your image matches these rules.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cgwalters Done in e31f74e: install now shares is_composefs_native() with lint (forge #23 folded in), the install docs, --composefs-backend help and setup-root-conf.toml(5) state the rule, and composefs CI images now ship the marker without prepare-root.conf and drop --composefs-backend. Validate, unit tests and plan-52 (ostree and composefs, at the pre-CI-change commit) passed; the tmt runs of the new no-flag CI path were lost to a devspace timeout and are re-running now.
Commits needing your approval + bot-pr signoff: 3555639, bd17b4f, 7ea45d5, 5e8ea5e, e31f74e (de19ffb, 217b7c7 unchanged, sign-off kept).

Generated-by: https://github.com/cgwalters/#llms

@github-actions github-actions Bot added the area/install Issues related to `bootc install` label Sep 30, 2026
Comment thread crates/lib/src/install.rs Outdated
Comment on lines 1753 to 1762
if !composefs_options.composefs_backend {
anyhow::ensure!(
!composefs_options.allow_missing_verity,
"--allow-missing-verity requires the composefs backend"
);
anyhow::ensure!(
composefs_options.uki_addon.is_none(),
"--uki-addon requires the composefs backend"
);
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have this kind of stuff in a .validate() method on the options, then it's easier to unit test option combos

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cgwalters Moved into InstallComposefsOpts::validate() (bootloader=none included), with a table-driven test test_composefs_opts_validate; in f8edc67.

Generated-by: https://github.com/cgwalters/#llms

Comment thread crates/lib/src/install.rs Outdated
if composefs_options.composefs_backend
&& matches!(config_opts.bootloader, Some(Bootloader::None))
{
if !composefs_explicit && !composefs_required {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh let's just drop this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped; the plain bootloader=none error now comes from validate().

Generated-by: https://github.com/cgwalters/#llms

Comment thread crates/lib/src/install.rs Outdated
r
// Only the ostree backend uses prepare-root.conf, and composefs-native
// images needn't have one.
let prepareroot_config = match prepareroot_config {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call this ostree_prepareroot_config to be clear

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to ostree_prepareroot_config (the State field too).

Generated-by: https://github.com/cgwalters/#llms

Comment thread docs/src/bootc-installation.7.md Outdated

### Selecting the storage backend

`bootc install` uses the ostree backend by default. It uses the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, let's say the storage backend is determined by the image, period. We have clear ways to select now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewritten: the backend is determined by the image, with no mention of the flag there (and the same hedge dropped from setup-root-conf.toml(5) and the composefs page); 0069a2d.

Generated-by: https://github.com/cgwalters/#llms

}

# Install `image` from itself and return the backend found on the disk
def install [image: string, ...args: string] {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is okay for now, we should eventually move this stuff into an explicit "install tests" suite as it conceptually has nothing to do with the host environment

And we need to dedeup the install-in-test code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a TODO linking cgwalters-forge/tracker#249, which covers both the install-tests suite and deduping the install-in-test code; the dedup isn't small, so it's left to that issue.

Generated-by: https://github.com/cgwalters/#llms

Comment thread Dockerfile Outdated
# setup-root-conf.toml, and there must be no ostree prepare-root.conf, so
# that `bootc install` picks composefs without --composefs-backend.
if [[ "${variant}" == composefs* ]]; then
rm -f /target-rootfs/usr/lib/ostree/prepare-root.conf /target-rootfs/etc/ostree/prepare-root.conf

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use -vf for logging

@cgwalters-bot cgwalters-bot Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (rm -vf). @cgwalters Plan-52 failed on composefs systemd-boot, because those images drop bootupd, which an ostree install needs. bce37b2 skips that one case there. It is the only commit on top of the head you approved, and it needs your approval and then bot-pr signoff. Tested at this head without --composefs-backend: tmt readonly and 52 pass on composefs systemd BLS. Readonly, 23, 32 and 52 passed on composefs grub BLS and on ostree, and validate and unit tests passed.
Question, from Copilot's lint comment: should baseimage-composefs use defaults_to_composefs_backend() instead of only the marker, so an image that keeps prepare-root.conf is linted as ostree?

Generated-by: https://github.com/cgwalters/#llms

cgwalters
cgwalters previously approved these changes Sep 30, 2026
auto-merge was automatically disabled October 1, 2026 14:01

Head branch was pushed to by a user without write access

@cgwalters-bot
cgwalters-bot force-pushed the bot/lint-ostree-symlink-composefs branch from 260f2dd to 55cf30c Compare October 1, 2026 14:01
@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

@cgwalters Rebased onto main (regenerated tmt fmf only; main took plan-52 for install-repart, so this test is now plan-60); ddb8f11 needs your approval for DCO.

Generated-by: https://github.com/cgwalters/#llms

cgwalters
cgwalters previously approved these changes Oct 1, 2026
@cgwalters-bot
cgwalters-bot force-pushed the bot/lint-ostree-symlink-composefs branch from 55cf30c to 1082e38 Compare October 1, 2026 14:14
@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

@cgwalters rebased onto current main. I dropped "tests-integration: Fix race between the install config tests" since #2520 now has the RwLock version of that fix. The other seven commits applied without conflicts and keep your sign-off, so none needs re-approval for DCO. Tested on a devspace: just validate, just unit-tests and just test-container pass.

Generated-by: https://github.com/cgwalters/#llms

@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

@cgwalters plan-32 (multi-device-esp) fails on the composefs grub legs with Filesystem does not support fs-verity (enabling verity on meta.json). The test formats a plain ext4 with mkfs.ext4 -q and runs bootc install to-existing-root with no backend flag. On main that is an ostree install. With this PR the image signals composefs, so the install becomes composefs, and bootc infers verity support from the configured fstype (ext4 counts as supported), so it creates a strict repository on a filesystem without verity. The #2314 repart.d change is not the cause; it still enables verity.

The semantic question: this PR changes the backend chosen for to-existing-root and to-filesystem onto filesystems the user formatted themselves, which may lack verity.

Options:

  • (A) The test formats with -O verity (test-only, five mkfs.ext4 calls).
  • (B) The test pins its backend explicitly.
  • (C) bootc checks the mounted target's actual verity support and falls back to relaxed mode, or errors with a hint (tune2fs -O verity / --allow-missing-verity).
  • (D) The composefs default applies only to to-disk, not to to-filesystem/to-existing-root.

I'd lean to (A) now to unblock CI, with (C) as the real fix, since it also covers users' own filesystems. (D) avoids the question but gives up the point of the marker. Your call; nothing is pushed. A local commit for (A) exists but has not been run or pushed.

Generated-by: https://github.com/cgwalters/#llms

@cgwalters

Copy link
Copy Markdown
Collaborator

Yes, I think clearly A.

This is an intended semantic change - our composefs based images now signal a composefs default to be desired.

The baseimage-root lint insists on an /ostree -> sysroot/ostree
symlink, and baseimage-composefs warns unless ostree's prepare-root.conf
enables composefs. Both are meaningless for images that are only ever
deployed with the composefs backend, and just force them to carry
ostree cruft.

Use the presence of /usr/lib/composefs/setup-root-conf.toml (even if
empty) as the signal that an image is composefs-native, mirroring how
prepare-root.conf signals ostree. If /ostree is present anyway it is
still validated, and /sysroot is still required since both backends
mount the physical root there.

Closes: bootc-dev#2256
Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
Prep for bootc install using the same signal to pick its default
backend.

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
Today only a UKI selects the composefs backend automatically. An image
built for composefs with a traditional kernel and initramfs (BLS) is
installed with ostree unless every caller passes --composefs-backend,
which bootc-image-builder and Anaconda don't.

The lints already treat /usr/lib/composefs/setup-root-conf.toml as the
marker of a composefs-native image. Use the same marker here: when an
image ships it and has no ostree prepare-root.conf, it can't be installed
with ostree anyway, so default to composefs. An image with both is still
installed with ostree by default, since it may be meant for either
backend: bootc's own composefs CI images, for example, add
setup-root-conf.toml to a stock base image and pass the flag.

Both files are read from the root bootc runs in, as the install
configuration and prepare-root.conf already were, including with
--source-imgref: bootc-image-builder runs bootc from the image it
installs, so it gets the same default.

Such an image also failed with --composefs-backend, since install
required prepare-root.conf regardless of the backend; only the ostree
backend reads it, so it's optional for composefs now.

--allow-missing-verity and --uki-addon used to require --composefs-backend
at the clap level, which would reject them for an image selecting the
backend by itself, so check them after the backend is decided.

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
Build a derived image that adds an empty setup-root-conf.toml and drops
ostree's prepare-root.conf, and check that `bootc install to-disk` run
from it installs the composefs backend without the flag, both as a
self-install and with --source-imgref as bootc-image-builder runs it.
An image that keeps prepare-root.conf must still get ostree. On the
ostree variant, nothing else selects composefs.

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
Image authors who want their image installed with composefs by default,
and only with composefs, need one place stating which rules the image
has to match; so far that was only spelled out in the composefs and
setup-root-conf.toml pages.

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
The composefs test images were only installed with composefs because
every CI path passed --composefs-backend, so CI never exercised the way
composefs-native images are meant to select the backend. Build them
like such an image instead: ship setup-root-conf.toml (empty unless a
baseconfig fills it), drop ostree's prepare-root.conf, and let install
pick composefs by itself. This covers the sealed and unsealed UKI
variants too; a UKI already selected composefs, and the marker doesn't
hurt there.

bcvk only takes --bootloader together with --composefs-backend, so the
images now name their bootloader in an install configuration file.
BOOTC_variant=composefs still selects the composefs plans and
filesystem in run-tmt. test-upgrade keeps passing the flag, since it
installs the published base image first, which isn't composefs-native.

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
The composefs systemd-boot test images drop bootupd, which an ostree
install requires, so installing the image that keeps prepare-root.conf
failed there with "bootupd is required for ostree-based installs". Only
run that case where bootupd is present; the composefs cases still run.

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
@cgwalters-bot
cgwalters-bot force-pushed the bot/lint-ostree-symlink-composefs branch from 46d2ca1 to 075b2a2 Compare October 1, 2026 19:15
@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

@cgwalters option A is pushed as 075b2a2 ("tests: Enable fs-verity on the multi-device ESP test's filesystems"), the only commit needing your approval. Rebased onto current main (conflicts in the generated tmt fmf files, resolved by regenerating; the earlier 7 commits keep your sign-off, and the plan-60 commit "tests: Install a composefs-native image without --composefs-backend" had its fmf hunks conflict-resolved, so please re-check that diff). plan-32 passes on composefs/grub/ext4 and ostree/grub with fedora-bootc:44, and just validate passes.

Generated-by: https://github.com/cgwalters/#llms

This test formats its own ext4 and runs `bootc install to-existing-root`
without a backend flag. Now that the composefs test images default to
the composefs backend, bootc infers verity support from the configured
fstype (ext4 counts as supported) and creates the repository in strict
mode, which fails on a plain mkfs.ext4 with "Filesystem does not
support fs-verity". Create the filesystems with -O verity.

Generated-by: AI
Signed-off-by: Colin Walters <walters@verbum.org>
@cgwalters-bot
cgwalters-bot force-pushed the bot/lint-ostree-symlink-composefs branch from 075b2a2 to 804f7d2 Compare October 1, 2026 19:29
@cgwalters
cgwalters enabled auto-merge (rebase) October 1, 2026 20:15
@cgwalters-bot

Copy link
Copy Markdown
Contributor Author

@cgwalters the one red job (fedora-44, ostree, xfs, grub, bls) is a flake unrelated to this PR: plan-21 (logically-bound-switch) rebooted into the old deployment because ostree-finalize-staged failed at shutdown with Remounting /boot read-write: Invalid argument right after boot.mount deactivated (a /boot unmount ordering race; the image there is ostree, untouched by this PR). The other 30 plans in that job and all other legs passed. Could you rerun the failed job? https://github.com/bootc-dev/bootc/actions/runs/36914679308/job/110562101666

Generated-by: https://github.com/cgwalters/#llms

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/documentation Updates to the documentation area/install Issues related to `bootc install`

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint: /ostree symlink required ➡️ thinking about composers-native signaling

4 participants