You tried dnf install and the terminal screamed read-only
You installed Fedora Silverblue because you wanted a system that updates without breaking. You opened a terminal and typed sudo dnf install git. The terminal printed Read-only file system. You searched the web and found sudo rpm-ostree install git. It worked. You rebooted and git appeared. You installed a second package, rebooted, and now your system is slow or the package vanished. You are treating Silverblue like Workstation. That approach fails. Silverblue manages packages differently. You need to understand layers, deployments, and the atomic model before you install anything else.
Stop guessing. Learn the atomic model now.
What is actually happening
On Fedora Workstation, the filesystem is mutable. dnf writes files directly to disk. It adds a binary here, removes a library there. The system changes incrementally. On Silverblue, the root filesystem is read-only. The system image is immutable. You cannot modify files in /usr. rpm-ostree is the tool that manages this image. It does not modify files. It builds new images.
When you run rpm-ostree install, the tool downloads the package, merges it with the base image, and creates a new deployment. This new deployment sits in a staging area. It does not affect the running system. The running system continues with the old image. The change only activates when you reboot. The reboot switches the boot loader to the new deployment.
Think of the system image like a master recording. You cannot edit the master recording while the concert is playing. Instead, you create a new mix with the changes. You test the new mix. When you are ready, you swap the tape deck. The audience hears the new mix on the next track. The old mix is still available if the new one has static.
rpm-ostree uses layers. A layer is a set of RPM packages added on top of the base image. You can add multiple layers. You can remove layers. The base image comes from Fedora. The layers come from you. If you remove a layer, the packages in that layer disappear on the next reboot. This is different from dnf remove. dnf remove deletes files. rpm-ostree uninstall removes the layer definition.
rpm-ostree status shows your deployments. The top entry is the current boot. The second entry is usually the previous boot, available as a rollback. This is a safety net. If an update breaks your system, you can boot the rollback and fix the issue. The rollback is automatic. You do not need to create snapshots manually.
The root is read-only. The layer is your playground.
How to manage packages with rpm-ostree
Here's how to add a package to your system image.
sudo rpm-ostree install htop tmux # WHY: Adds packages to the layer. The system image is rebuilt in the background.
The command downloads the packages and builds a new deployment. It prints Deployment complete. when finished. The package is not active yet. The running system still uses the old image.
The package is staged but not active yet. You must reboot to switch to the new deployment.
systemctl reboot # WHY: Reboots into the staged deployment. The new image becomes the running system.
After the reboot, the new image is running. The packages are available. You can verify this by running the command or checking the status.
Removing a package requires removing the layer, not just deleting files.
sudo rpm-ostree uninstall htop # WHY: Removes the package from the layer definition. The next reboot will drop the package.
The command removes the package from the layer. The package remains in the running system until you reboot. After the reboot, the package is gone.
Upgrading the base OS is different from installing packages.
sudo rpm-ostree upgrade # WHY: Pulls the latest base image and applies your layers on top. Stages the update for reboot.
This command updates the base image and refreshes your layers. It stages the update for the next reboot. Reboot to apply the update.
Old deployments take up disk space. Clean them up periodically.
sudo rpm-ostree cleanup --rollback # WHY: Removes old rollback deployments to free disk space. Keep one rollback for safety.
This command removes old deployments. It keeps the current boot and one rollback. This frees disk space while maintaining the safety net.
rpm-ostree upgrade is the weekly maintenance command. It updates the base and refreshes layers. rpm-ostree rebase changes the base image reference. Use upgrade for routine updates. Use rebase to switch to a different image, like moving from Silverblue to Kinoite.
Reboot after every install. The package isn't real until you boot the new image.
Verify it worked
Check what deployments are available and which one is running.
rpm-ostree status # WHY: Lists deployments. Look for the "Pinned" or "Deployed" markers. The top entry is the current boot.
The output shows a list of deployments. The top entry is the current boot. Look for Layered packages: in the output. Your packages should be listed there. If you see your packages, the layer is active.
Confirm the layer is active and the package is available.
rpm-ostree status --json | jq '.deployments[0].packages' # WHY: Parses the status output to show only layered packages. Requires jq.
This command prints the list of layered packages in the current deployment. If the package is in the list, it is active. If the package is missing, check the previous deployment or the journal for errors.
Trust the status output. If the package isn't in the layered list, it won't survive a reboot.
Common pitfalls and what the error looks like
You will see Read-only file system if you try to write to /usr. This is expected. Use rpm-ostree instead of dnf.
You will see Error: Transaction test error if there is a conflict.
Error: Transaction test error: package python3-3.12.x conflicts with python3-3.13.y
This error means the package you want cannot coexist with the base image. rpm-ostree refuses to break the base image. You cannot force this. You must find a compatible package or use a container.
Do not use dnf on the host. dnf will fail or produce unpredictable results. dnf expects a mutable filesystem. Silverblue provides a read-only filesystem. dnf cannot modify the system. Use rpm-ostree for system packages.
If you need dnf, use toolbox. Toolbox gives you a mutable container where dnf works normally. You can install packages in the container without affecting the host. The container is isolated. The host remains immutable.
rpm-ostree builds an image. This takes time. Do not interrupt the process. Wait for the Deployment complete. message. If you interrupt the build, the deployment may be corrupted. You can recover by booting the rollback and retrying.
For advanced users, rpm-ostree ex exposes the underlying ostree commands. Use rpm-ostree ex when you need to manipulate commits directly or debug low-level image issues. This is for experts. Most users do not need ex.
Read the conflict message. rpm-ostree refuses to break the base image. Fix the conflict, don't force it.
When to use rpm-ostree vs alternatives
Use rpm-ostree install when you need a package available system-wide, like a kernel module or a CLI tool used by scripts.
Use toolbox when you need a mutable environment for development, compiling code, or installing packages that conflict with the base image.
Use flatpak when you want a desktop application with its own dependencies, isolated from the system.
Use rpm-ostree upgrade when you want to update the OS and keep your layers intact.
Use rpm-ostree rebase when you want to switch to a different OS image, like moving from Silverblue to Kinoite.
Use rpm-ostree ex when you need to manipulate commits directly or debug low-level image issues.
Layer only what the system needs. Everything else goes in a container or a Flatpak.