Bootc Image Switching and Deployment#
bootc switch and bootc upgrade operate in an A/B style — changes are staged against the running system and applied only at next boot. A queued update appears as staged in bootc status output; the running OS is never modified mid-session.
The Staging Lifecycle#
| Phase | What happens |
|---|---|
bootc switch <image> / bootc upgrade | New OCI image layers are downloaded and a new deployment is staged |
| Running → Shutdown | System runs normally; staged deployment waits |
ostree-finalize-staged.service | Executes at shutdown time — performs the 3-way /etc merge and finalizes the bootloader entry for the new deployment |
| Next boot | System boots into the new image |
The /etc 3-way merge preserves local customizations: it diffs the old default vs. the current /etc and applies that diff on top of the new default /etc, including uid/gid and xattr metadata.
⚠️ Future behavior note: The bootc docs indicate the automatic-at-shutdown apply behavior may change, requiring
bootc upgrade --applyto explicitly trigger application.
Key bootc switch Options#
| Flag | Effect |
|---|---|
--enforce-container-sigpolicy | Verifies image signatures (recommended; replaces deprecated --target-no-signature-verification) |
--apply | Reboots immediately after staging |
--soft-reboot=auto | Uses soft reboot (no hardware reset) when available |
--retain | Keeps a reference to the currently booted image |
--download-only | Stages without queuing for application; activate later with bootc upgrade --from-downloaded |
Checking Current Deployment State#
sudo bootc status
Example output shows staged, booted, and rollback entries. The image reference (e.g., ghcr.io/projectbluefin/bluefin:stable) and tag are the key fields to note before any switch.
Cross-Family Switching Constraints#
Switching between the Fedora-based Bluefin family and the CentOS Stream 10-based Bluefin LTS / Dakota family is not supported.
These are fundamentally different operating systems. The key technical blockers:
| Incompatibility | Detail |
|---|---|
| Package manager | CentOS Stream 10 ships dnf 4.x — no dnf5, which Fedora-based build/install scripts depend on |
$releasever resolution | Fedora .repo files use $releasever which doesn't map to CentOS Stream 10 — silently breaks repository paths |
| COPR chroot naming | epel-10 vs centos-stream-10 chroots differ, causing package availability gaps |
libdnf4 variable expansion | Older libdnf4 on CentOS-based images can't expand ${releasever_minor:+-z} syntax in EPEL 10 metalink URLs, causing HTTP 404s |
Supported paths:
- Within the same family — use
bootc switchorujust rebase-helperfreely across variants (e.g.,bluefin:stable→bluefin-nvidia-open:stable) - Across families — plan as a fresh install, or use the automated migration service (see below)
Automated Migration: ublue-os → projectbluefin#
For users on the legacy ghcr.io/ublue-os/bluefin-lts images, a systemd-based migration service handles the registry switch automatically. It ships inside the old image and runs as a one-shot service on a daily retry timer.
Variant mapping:
Old image (ublue-os) | New image (projectbluefin) |
|---|---|
bluefin-gdx:lts* | bluefin-lts-nvidia:stable |
bluefin-dx:lts* | bluefin-lts:stable |
bluefin:lts* | bluefin-lts:stable |
| arm64 | MOTD only — reinstall required |
The service calls bootc switch --enforce-container-sigpolicy <new-image> (non-destructive until reboot), writes a MOTD next-reboot notice, and touches a stamp file to prevent re-runs. This MOTD is absent from the new image and self-cleans on first boot into it.
To migrate manually before the service ships:
sudo bootc switch ghcr.io/projectbluefin/bluefin-lts:stable --enforce-container-sigpolicy