How I Set Up a Full Dev Environment on Fedora Silverblue (Without Touching the Host)
Fedora Silverblue is immutable — you can't apt install or pip install on the host. Here's how I use distrobox to run a full Ubuntu dev environment with Rust, Node, Python, and AI coding tools, all without compromising the base OS. Fedora Silverblue is an immutable desktop OS — the base filesystem is read-only and atomic. That's great for reliability, but it means you can't This post covers why I chose this setup, how to build it, and how to make it reproducible. Jump to the setup → · Skip to the reproducible manifest → I run Fedora Silverblue on my daily driver — an ASUS ROG Strix with a Ryzen 9 and RTX 5070 Ti. The pitch is simple: your OS is an atomic image, like a phone OS. Updates are transactional. If something breaks, you reboot into the previous image. You can't accidentally bork your system by installing a conflicting package. After years of traditional Linux where a bad kernel module or a library conflict could eat an afternoon, I wanted the "it just works" layer to be actually unbreakable. Silverblue gives me that. But there's a tradeoff. On Silverblue, the root filesystem ( For development — where you need Toolbox is Fedora's official answer — it creates a Fedora container that integrates with your host. It works fine, but it's Fedora-only by default and less configurable. Distrobox is the same concept, broader: Why not a VM? Overhead. A VM needs its own kernel, its own memory allocation, its own disk image. Distrobox shares the host kernel, the host filesystem, the host GPU driver. There's no performance penalty — your compiler runs at native speed because it is running natively, just in a different userspace. Distrobox is a set of shell scripts. Install it to your home directory (which is mutable on Silverblue): Make sure This pulls the Ubuntu 24.04 image and creates a container named Your prompt changes to Now you have This gives you a C/C++ toolchain, Python with pip and venvs, git, and networking/diagnostic tools. The These go in Node.js via nvm: Rust via rustup: Cargo dev tools: If you want to read host systemd journals and DNS state from inside your container, you can add init hooks. These bind-mount host paths into the container on every start: With these mounts, The manual setup works, but if you ever need to rebuild (or set up a second machine), you don't want to remember 30 commands. Distrobox has A few things to note about this format — I had to read the The manifest handles apt packages (the container layer), but tools installed in That's it. New machine, clean home directory, two commands to a fully working dev environment. The mental model: your host is the stable foundation. Your container is the disposable toolbox. Your home directory is the persistent workspace that bridges them. Inside the container, you get: It doesn't feel like a container. It feels like your machine. That's the point. The distrobox manifest and bootstrap script are in my config-management repo under TLDR
apt install gcc or pip install flask on the host. Distrobox solves this by giving you a full mutable Linux userspace (Ubuntu, Fedora, Arch — your pick) inside a container that feels like your native system. Your home directory, Wayland display, D-Bus, GPU — all shared.In This Post
Why Silverblue?
The Problem: Immutable Means No Package Manager
/usr, /etc) is read-only. There's no dnf install on the host. The official package management options are:gcc, python3-pip, libssl-dev, node, cargo, and a hundred other things — the container approach is the right one. Keep the host clean, do all your messy dev work in a container you can nuke and rebuild.Why Distrobox (and Not Something Else)?
.deb from a vendor? It just works.distrobox assemble. An INI manifest that declaratively defines your container — image, packages, hooks. One command to rebuild.The Setup
1. Install distrobox
|
~/.local/bin is in your PATH (it is by default on Silverblue).2. Create your dev container
dev. First run takes a minute while distrobox sets up the host integration layer.3. Enter it
📦[you@dev ~]$ and you're in Ubuntu. Your home directory is right there. cd ~/projects works. git status works (once you install git).4. Install your dev packages
apt. Go wild: &&
sudo here is inside the container — it doesn't touch your host.5. Install language runtimes in your home directory
$HOME, so they survive container rebuilds: |
|
6. Add host system observability (optional)
# These go in the distrobox assemble manifest (next section)
# or you can run them manually:
journalctl inside the container reads host logs. Handy if you're debugging system-level issues without leaving your dev shell.Making It Reproducible
distrobox assemble — an INI manifest that captures your container definition.The manifest:
dev.ini[dev]
docker.io/library/ubuntu:24.04
true
true
build-essential cmake pkg-config libssl-dev
python3 python3-pip python3-venv python3-dev
git curl wget rsync unzip zip less lsof bc time tree dialog
mtr traceroute tcpdump iputils-ping iproute2 openssh-client
ca-certificates gnupg man-db manpages
mesa-vulkan-drivers libvulkan1 libegl1 libegl-mesa0 libgl1 libglx-mesa0 xauth
"mount --rbind /run/host/run/systemd/journal /run/systemd/journal 2>/dev/null || true"
"mount --rbind /run/host/run/systemd/resolve /run/systemd/resolve 2>/dev/null || true"
"mount --rbind /run/host/run/systemd/seats /run/systemd/seats 2>/dev/null || true"
"mount --rbind /run/host/run/systemd/sessions /run/systemd/sessions 2>/dev/null || true"
"mount --rbind /run/host/run/systemd/users /run/systemd/users 2>/dev/null || true"
"mount --rbind /run/host/var/lib/systemd/coredump /var/lib/systemd/coredump 2>/dev/null || true"
"mount --rbind /run/host/var/log/journal /var/log/journal 2>/dev/null || true"
"mount --rbind /run/host/etc/localtime /etc/localtime 2>/dev/null || true"
distrobox-assemble source to figure it out:= line by line — no multiline values.additional_packages= lines get joined internally. Same for init_hooks=.&& in the generated command, so if one mount fails (the || true handles that), the rest still run.replace=true means running assemble again tears down and recreates the container. Your home directory (a bind mount) is untouched.The bootstrap script:
bootstrap-dev-tools.sh$HOME need a separate script:#!/usr/bin/env bash
# Install user-space dev tools into $HOME.
# Run INSIDE the distrobox after assemble create.
# Idempotent — safe to re-run.
# Node.js via nvm
if [; then
|
fi
[ &&
# npm global packages
# Rust via rustup
if ! ; then
|
fi
# Cargo dev tools
Rebuild in two commands
# From the host:
# Enter and bootstrap (only needed on fresh $HOME):
What You End Up With
Layer What Where Survives rebuild? Host Silverblue + GPU drivers + Flatpak apps / (immutable)Always Container Ubuntu 24.04 + apt packages Container overlay No — rebuilt from manifest Home nvm, rustup, cargo bins, dotfiles, repos ~/ (bind mount)Yes configs/distrobox/ if you want to use them as a starting point.