You upgraded and the desktop won't load
You ran the update command, the terminal printed a progress bar, and you rebooted. The system paused longer than usual, dropped you at a GRUB menu with two identical-looking entries, and then loaded a desktop that looks exactly the same as before. You check the terminal and see a message about deployments and commits instead of the familiar package list. You are not broken. You are just looking at a different update model.
The traditional Fedora Workstation modifies files in place. Every dnf upgrade replaces binaries, libraries, and configuration files directly on the running filesystem. That model works fine until a partial download, a power loss, or a dependency conflict leaves the system in a half-updated state. OSTree flips that model on its head. It treats the entire operating system as a single version-controlled tree. Updates are downloaded as complete, immutable snapshots. The system boots into the new snapshot only after you reboot. If the new snapshot fails, you boot back into the old one. The filesystem never sits in a transitional state.
Reboot before you debug. Half the time the symptom is gone.
What OSTree actually does
OSTree stands for Operating System Tree. It borrows the commit and branch model from Git, but applies it to filesystem images instead of source code. Every Fedora Silverblue or Kinoite release is built as a single OSTree commit. That commit contains every file that belongs to the base system. The commit hash acts as a fingerprint. If even one byte changes, the hash changes.
When you request an update, the system does not patch individual files. It downloads the entire new commit as a compressed tarball, verifies its signature, and extracts it into a separate directory on disk. The running system continues to use the old commit. The new commit sits idle until you reboot. At boot time, the bootloader points the kernel and initramfs at the new commit directory. The old commit remains untouched on disk. You can switch back to it instantly.
This model guarantees a known-good state. You never boot into a system where half the libraries are version 3.12 and the other half are version 3.13. The tradeoff is disk space. OSTree keeps the previous deployment intact so you can roll back. A typical Silverblue installation uses roughly 15 to 20 gigabytes for the base tree, plus another 10 to 15 gigabytes for the previous deployment and layering. Plan your partition accordingly.
Config files follow a strict convention. Files in /usr/lib/ ship with the package and belong to the OSTree commit. You never edit them. Files in /etc/ are user-modified. OSTree merges /etc/ on top of the read-only /usr/ at boot time using a union mount. Your customizations survive every update. If you accidentally edit a file in /usr/lib/, the next update will overwrite it. Trust the package manager. Manual file edits drift, snapshots stay.
How rpm-ostree manages the tree
The rpm-ostree command is the interface between you and the OSTree backend. It handles downloading commits, verifying signatures, managing deployments, and applying layers. You will use it instead of dnf for system-wide package changes. The workflow is deliberate. You stage changes, verify them, and then reboot to activate them.
Here is how to check your current deployment and see what is pending.
rpm-ostree status
# Shows the active commit hash, the deployment index, and pending updates
# The "Deployments" section lists every snapshot currently on disk
# The "State" line tells you if the system is clean or waiting for a reboot
When the output shows State: idle, the system is running a stable commit. When it shows State: deploying, a new tree is staged but not yet active. You must reboot to switch trees. The rpm-ostree upgrade command fetches the latest commit for your current branch and stages it.
sudo rpm-ostree upgrade
# Contacts the configured repository and downloads the new commit
# Verifies the GPG signature and extracts the tree to a new deployment slot
# Does not modify the running filesystem. Changes take effect after reboot
If you need to switch Fedora releases, you use rebase instead of upgrade. The upgrade command stays on the same branch (for example, fedora/silverblue/x86_64/41). The rebase command points the system to a different branch (for example, fedora/silverblue/x86_64/42). The tool downloads the new base commit and stages it alongside your existing layers.
sudo rpm-ostree rebase fedora/silverblue/x86_64/42
# Switches the target branch to Fedora 42
# Preserves your layered packages and /etc/ configuration
# Stages the new base tree for activation on next boot
You can add packages that do not exist in the base tree by layering them. Layering installs RPMs on top of the immutable base. The packages are tracked separately from the base commit. You remove them with rpm-ostree uninstall.
sudo rpm-ostree install firefox
# Downloads the RPM and its dependencies from the configured repos
# Places them in a layer directory that mounts over the base tree
# The base commit remains untouched. You can remove the layer later
Run reboot immediately after staging. The bootloader will present the new deployment as the default entry. If the system fails to boot, the GRUB menu will still list the previous deployment. Select it manually, or let the fallback timeout trigger automatically.
Snapshot the system before the upgrade. Future-you will thank you.
Verify the deployment
After the reboot, confirm that the new commit is active and that your layers survived the transition. The status command shows the deployment order. The first entry is the active tree. The second entry is the previous tree, kept for rollback.
rpm-ostree status
# Confirms the active commit hash matches the new release
# Lists layered packages under "Packages"
# Shows the deployment index. Index 0 is active. Index 1 is the fallback
If the desktop loads but a service fails, check the journal. The journalctl -xe command adds explanatory context and jumps to the end of the log. Most sysadmins type journalctl -xeu <unit> to isolate a specific service. Read the actual error before guessing.
journalctl -xeu NetworkManager.service
# Shows recent log lines for the unit with explanatory markers
# The 'x' flag highlights failed operations. The 'e' flag jumps to the end
# Use this before restarting services or editing configuration
If you see [FAILED] Failed to start NetworkManager.service during boot, your network configuration probably references a missing interface name. Check /etc/NetworkManager/ for stale connection profiles. Delete or rename the offending file, then reload the daemon.
sudo nmcli connection reload
# Forces NetworkManager to re-read /etc/NetworkManager/system-connections/
# Applies new profiles without requiring a full system reboot
# Runtime and persistent configurations stay synchronized
Check the deployment index after every major change. If the active index jumps unexpectedly, a background update or a failed transaction staged a new tree. Verify the commit hash before proceeding.
Run journalctl first. Read the actual error before guessing.
Common pitfalls and error messages
The most frequent mistake is trying to use dnf on the host system. The base filesystem is read-only. dnf will refuse to write to /usr/ and abort with a clear error.
Error: Transaction test error:
file /usr/lib/systemd/system/foo.service from install of foo-1.0-1.fc41.x86_64 conflicts with file from package ostree-base-1.0-1.fc41.x86_64
The conflict is intentional. The base tree owns /usr/. Use rpm-ostree install for system packages, or use a container for development tools. The error message tells you exactly which file is contested. Do not force the transaction. Forcing breaks the immutable guarantee and leaves the tree in an unbootable state.
Another common issue is layering a package that already exists in the base tree. OSTree will warn you and skip the layer. The base tree always wins. If you need a newer version of a base package, you must wait for the next Fedora release or use a container.
Warning: Package 'coreutils' is already provided by the base tree.
Skipping layer. Use 'rpm-ostree override replace' if you intentionally want to shadow the base package.
Shadowing the base tree is dangerous. It replaces a base file with a different RPM version. The next rpm-ostree upgrade may fail because the new base commit expects the original file. Only shadow when you understand the dependency chain and accept the maintenance burden.
SELinux denials also surface frequently on immutable systems. The policy expects files in specific locations. If you place a script in /usr/local/bin/ and run it, SELinux may block execution because the context does not match. Check the denial log before disabling the policy.
journalctl -t setroubleshoot
# Shows one-line summaries of SELinux access denials
# Includes the affected file, the process, and the suggested fix
# Read the summary before running restorecon or disabling enforcement
If the boot menu is gone, GRUB rescue is your friend, not your enemy. Use the ls command to find the OSTree partition, set the root, and manually chainload the bootloader. Keep a live USB handy for filesystem checks.
Trust the package manager. Manual file edits drift, snapshots stay.
Choose the right workflow
Use Silverblue when you want a known-good base image you can always roll back to. Use Workstation when you need to install kernel modules or low-level driver patches. Use Server when you are running services and not a desktop. Stay on the upstream Workstation if you only deviate from the defaults occasionally. Use Toolbox when you need a mutable environment for compiling code or testing packages. Use Flatpak when you want sandboxed desktop applications that update independently of the OS. Use rpm-ostree when you manage the host system on an immutable branch. Use dnf when you manage a traditional mutable installation.
Pick the tool that matches your tolerance for change. Immutable systems reward patience. Mutable systems reward speed.