You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
bootc-fetch-apply-updates.timer/service never run on the composefs backend (/run/ostree-booted condition) #2578
On a composefs-backend (sealed / UKI) install, the automatic update units never run:
$ systemctl status bootc-fetch-apply-updates.timer
○ bootc-fetch-apply-updates.timer - Apply bootc updates
Loaded: loaded (/usr/lib/systemd/system/bootc-fetch-apply-updates.timer; enabled; preset: enabled)
Active: inactive (dead)
Trigger: n/a
Triggers: ● bootc-fetch-apply-updates.service
Condition: start condition unmet at ...; 8min ago
└─ ConditionPathExists=/run/ostree-booted was not met
/run/ostree-booted is only created by the ostree backend (ostree-prepare-root). On the composefs backend that service is skipped (ConditionKernelCommandLine=ostree) and no equivalent marker is created, so both bootc-fetch-apply-updates.timer and bootc-fetch-apply-updates.service carry an unmet ConditionPathExists=/run/ostree-booted and stay inactive forever.
Impact
Hosts installed with the composefs backend (e.g. sealed images booting a signed UKI) never fetch or apply updates automatically; they need an explicit bootc upgrade. The units are shipped enabled and there is no composefs-specific equivalent, so this is silent.
and the same drop-in for bootc-fetch-apply-updates.service. Verified: the timer then becomes active and schedules normally.
Suggested fix
Either drop the condition and let bootc itself decide whether the host is bootc-managed, or use a marker both backends provide (the composefs backend already creates /ostree/bootc; a /run/bootc-booted-style marker or a bootc status check would work).
The issue is that for the units systemd/bootc-fetch-apply-updates.timer/service, and also systemd/bootc-status-updated.target all three carry:
ConditionPathExists=/run/ostree-booted
crates/lib/src/generator.rs already documents this exact gap and works around it for other units:
// === Relabel unit: runs for transient composefs boots (native or ostree) ===
// Must be before the ostree-booted guard because native composefs boots do
// not write /run/ostree-booted, but still need the relabel unit when any
// transient overlay is active.
// === Shadow sync unit: runs for ALL bootc systems (native composefs or ostree) ===
// Must be before the ostree-booted guard because native composefs boots do
// not write /run/ostree-booted, but still need the shadow sync to clean up
// stale shadow/gshadow entries (the rechunk scenario). Gate on either a
// composefs mount source (native composefs boot) or the ostree-booted marker.
So the pattern is established: gate on either a composefs mount source (composefs: / transient:composefs=) or the /run/ostree-booted marker. The fetch-apply units (and bootc-status-updated.target) were simply missed, so on native composefs installs:
bootc-fetch-apply-updates.{timer,service} never start (no automatic updates), and
bootc-status-updated.target never activates, so cached bootc status data is not refreshed on changes.
Suggested fix: apply the same composefs-or-marker gate to those three units (or write /run/ostree-booted on native composefs boots). Repro is a plain bootc install to-disk from a sealed/composefs image.
Triage: the bug is valid and the cause is clear. It is a duplicate of the open draft #2437, which is blocked on a design decision, not on code.
Cause:bootc-fetch-apply-updates.timer, .service and also bootc-status-updated.target all have ConditionPathExists=/run/ostree-booted. Only ostree creates that file. Native composefs boots never write it, which generator.rs already works around for its own units. I didn't boot a test system; the reporter's systemctl status output matches what the code would do.
Why it isn't a one-line fix: In the #2437 review, @cgwalters raised a compatibility concern. Existing composefs users may depend on the timer not running because they bring their own update tooling, so turning it on now silently changes behaviour for them. Turning the timer off for ostree would break things the other way. There was no agreed way out of this "without VERY LOUD warnings on either side".
The current Remove condition on existance of /run/ostree-booted for bootc systemd units/timers #2437 diff (ConditionKernelCommandLine=|composefs) is incomplete. As noted in that review, Add support for multiple composefs= digests #2248 (now merged) added the composefs.digest= karg, and kernel command lines that use only that karg won't match. The check suggested in review is better: test whether / is mounted from composefs, as the generator does, or add a /run/composefs-booted marker. Either way, bootc-status-updated.target should get the same change.
What a fix needs:
A maintainer decision on the default: enable on composefs (with a release note, and maybe a warning) or keep it off and document how to enable it.
A reliable composefs-booted signal: a /run/composefs-booted written early, or a generator that turns the units on when it detects composefs.
Update all three units and add a composefs integration test checking that the timer is active.
Unclear: whether composefs users depend on the current no-auto-update behaviour, and so which default to pick. Until that's decided, the reporter's drop-in that clears ConditionPathExists= is a fine opt-in workaround.
Activity
SNThrailkill commentedon Oct 18, 2026
The issue is that for the units
systemd/bootc-fetch-apply-updates.timer/service, and alsosystemd/bootc-status-updated.targetall three carry:crates/lib/src/generator.rsalready documents this exact gap and works around it for other units:So the pattern is established: gate on either a composefs mount source (
composefs:/transient:composefs=) or the/run/ostree-bootedmarker. The fetch-apply units (andbootc-status-updated.target) were simply missed, so on native composefs installs:bootc-fetch-apply-updates.{timer,service}never start (no automatic updates), andbootc-status-updated.targetnever activates, so cachedbootc statusdata is not refreshed on changes.Suggested fix: apply the same composefs-or-marker gate to those three units (or write
/run/ostree-bootedon native composefs boots). Repro is a plainbootc install to-diskfrom a sealed/composefs image.cgwalters commentedon Oct 23, 2026
See #2437
cgwalters-bot commentedon Oct 23, 2026
cgwalters-bot commentedon Oct 23, 2026
cgwalters-bot commentedon Oct 23, 2026
Triage: the bug is valid and the cause is clear. It is a duplicate of the open draft #2437, which is blocked on a design decision, not on code.
bootc-fetch-apply-updates.timer,.serviceand alsobootc-status-updated.targetall haveConditionPathExists=/run/ostree-booted. Only ostree creates that file. Native composefs boots never write it, whichgenerator.rsalready works around for its own units. I didn't boot a test system; the reporter'ssystemctl statusoutput matches what the code would do.@cgwaltersraised a compatibility concern. Existing composefs users may depend on the timer not running because they bring their own update tooling, so turning it on now silently changes behaviour for them. Turning the timer off for ostree would break things the other way. There was no agreed way out of this "without VERY LOUD warnings on either side".ConditionKernelCommandLine=|composefs) is incomplete. As noted in that review, Add support for multiple composefs= digests #2248 (now merged) added thecomposefs.digest=karg, and kernel command lines that use only that karg won't match. The check suggested in review is better: test whether/is mounted fromcomposefs, as the generator does, or add a/run/composefs-bootedmarker. Either way,bootc-status-updated.targetshould get the same change.What a fix needs:
/run/composefs-bootedwritten early, or a generator that turns the units on when it detects composefs.Unclear: whether composefs users depend on the current no-auto-update behaviour, and so which default to pick. Until that's decided, the reporter's drop-in that clears
ConditionPathExists=is a fine opt-in workaround.