Move to Ubuntu 26.04 LTS: backup, setup and restore scripts

Replaces the Bazzite/BlueBuild image with plain Ubuntu. apt covers the system
and dev tools, Flathub the desktop apps, and three scripts do the rest:
backup.sh archives this machine's home to server-marvin and reads it back to
verify it; setup.sh builds a fresh 26.04 install (NVIDIA 595-open, Docker with
GPU access, Steam/Lutris, 27 Flatpaks, VS Code, Claude desktop, the Data drive
and marvin mounts); restore.sh unpacks the backup without the Fedora/KDE parts
that would fight Ubuntu.

The app list comes from a scan of the running machine. The 15 Flatpaks left
behind sit commented at the bottom of flatpaks.txt, and their data still rides
along in the backup. No Node or .NET on the host by choice — LudosData and
landingPage build in Docker.

Drops the BlueBuild recipe, the cosign key and the old dev-setup script. The
Bazzite setup stays in this history.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 15:48:47 -04:00
co-authored by Claude Opus 5
parent cf3fc8bb79
commit 1ddc56800a
10 changed files with 638 additions and 370 deletions
+145 -169
View File
@@ -1,189 +1,165 @@
# main-desktop
Custom [Bazzite DX](https://github.com/ublue-os/bazzite-dx) image for my desktop, defined with
[BlueBuild](https://blue-build.org). **There is no published image** — clone this repo and build
it yourself; `bluebuild switch` rebases the running system straight onto the local build.
**Ubuntu 26.04 LTS** on my desktop (Ryzen 7 9800X3D, RTX 5090, 49" 5120x1440). This replaces the
Bazzite setup, which lives on in this repo's git history.
- **Base:** `ghcr.io/ublue-os/bazzite-dx-nvidia` — Bazzite DX, KDE Plasma, **NVIDIA open kernel
modules**. There is no separate `-open` DX image anymore: DX NVIDIA is open-driver-only, which
is exactly what the RTX 5090 (Blackwell) requires. Current stable ships driver **610.x** (open).
- **Pinned** to Fedora 44 via `image-version: stable-44` in
[recipes/recipe.yml](recipes/recipe.yml). No surprise major upgrades; bump to `stable-45`
deliberately.
- **This image layers almost nothing.** The base already includes Steam and the gaming stack,
Flatpak, Docker CE + CLI, Podman, VS Code, Homebrew, **and nvidia-container-toolkit with
automatic CDI setup** (`ublue-nvctk-cdi.service` regenerates `/etc/cdi/nvidia.yaml` each boot).
What this recipe adds: a declarative first-boot Flatpak set (and a dormant signing policy).
That's it — by design.
This is plain Ubuntu with no custom image: apt handles the system and dev tools, Flathub handles
the desktop apps, and three scripts do the rest.
Why BlueBuild instead of a raw Containerfile: the whole custom layer is "install these Flatpaks
on first boot", which the `default-flatpaks` module expresses declaratively — and
`bluebuild switch` gives a one-command build-and-rebase with no registry involved.
## Getting onto this image (fresh desktop rebuild)
1. Install stock **Bazzite DX (NVIDIA)** from the official ISO at
[bazzite.gg](https://bazzite.gg) and boot into it.
2. Install the BlueBuild CLI and switch:
```bash
git clone https://git.lazypugs.com/ckoch/main-desktop.git && cd main-desktop
podman run --pull always --rm ghcr.io/blue-build/cli:latest-installer | bash
bluebuild switch recipes/recipe.yml # add --reboot to reboot when done
```
`switch` builds the image locally, stores it as an oci-archive under `/etc/bluebuild/`, and
rebases `rpm-ostree` onto it. First build pulls the multi-GB base — give it time and disk
(~25 GB free).
3. Reboot. Done — the first-boot service then installs the Flatpak list below (needs a few
minutes on first login; check with `flatpak list --system`).
Alternatively, `bluebuild generate-iso recipes/recipe.yml` can produce installer media with the
custom image baked in, skipping step 1's stock install.
**Updating later** (pulls the newest Bazzite `stable-44` base plus any recipe changes):
```bash
cd main-desktop && git pull && bluebuild update recipes/recipe.yml --reboot
```
Roll back anytime with `rpm-ostree rollback` (or pick the previous deployment in the boot menu).
Note on signing: local oci-archive rebases are inherently unsigned (`ostree-unverified-image`),
so cosign doesn't apply here. The recipe keeps the `signing` module and `cosign.pub` so that
publishing signed images to a registry later is just a `bluebuild build --push` away.
## Install-time checklist
1. **Driver sanity:** `nvidia-smi` reports driver **≥ 580** (currently 610.x) and the RTX 5090 is
listed. Confirm open kernel modules: `modinfo -F license nvidia` should say `Dual MIT/GPL`
(the proprietary module says `NVIDIA`). Blackwell only works on the open modules.
2. **Deployment:** `rpm-ostree status` shows the booted deployment is the local build
(`ostree-unverified-image:oci-archive:/etc/bluebuild/...`) with the expected base version.
3. **Flatpaks:** all installed (`flatpak list --system` — 22 apps from the list below). The
first-boot install is sequential and multi-GB; on slow links it can take 15+ minutes after
first login. Progress: `journalctl -b --grep flatpak -f`.
4. **Docker group:** run `ujust dx-group` (adds you to `docker` and friends), then log out/in.
5. **GPU in containers:** run the one-liner test in the next section.
## GPU in containers (CUDA on the 5090)
`nvidia-container-toolkit` is already in the image and a boot service generates the CDI spec
automatically. CDI is the shipped mechanism:
```bash
# Podman — works out of the box:
podman run --rm --device nvidia.com/gpu=all docker.io/nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi
# Docker — CDI device syntax also works on current Docker CE:
docker run --rm --device nvidia.com/gpu=all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi
```
If you want the classic `docker run --gpus all` syntax, that needs a one-time runtime hookup
(this writes to `/etc/docker/daemon.json`, which is mutable and survives updates):
```bash
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
```
## First-boot Flatpaks
Installed system-wide from Flathub by the `default-flatpaks` module. All IDs verified against
Flathub (2026-07).
**Official / verified listings:** Bambu Studio, Bitwarden, Discord, Firefox, Thunderbird,
FreeCAD, GIMP, HandBrake, LibreOffice, Moonlight, PCSX2 (`net.pcsx2.PCSX2` — the official
build), Podman Desktop, Prism Launcher, Warzone 2100, XIVLauncher (`dev.goats.xivlauncher`).
**Community / unverified listings — know what you're running:**
| App | ID | Note |
| Script | Where it runs | What it does |
|---|---|---|
| Android Studio | `com.google.AndroidStudio` | Community packaging of Google's IDE (not verified) |
| GitHub Desktop | `io.github.shiftey.Desktop` | Community Linux fork; the "shiftey" spelling is the real ID |
| MakeMKV | `com.makemkv.MakeMKV` | Community packaging of the proprietary app |
| GeForce NOW | `io.github.hmlendea.geforcenow-electron` | **Unofficial** Electron wrapper, not an NVIDIA product |
| Spotify | `com.spotify.Client` | Community packaging of the proprietary client |
| VLC | `org.videolan.VLC` | No verified badge on Flathub (still the VideoLAN-maintained build) |
| Godot | `org.godotengine.Godot` | No verified badge on Flathub |
| [scripts/backup.sh](scripts/backup.sh) | Bazzite, **before wiping** | Archives your home folder to server-marvin and reads every archive back to check it |
| [scripts/setup.sh](scripts/setup.sh) | Fresh Ubuntu | Adds repos, the NVIDIA driver, Docker, Steam, Flatpaks, dev tools and mounts, all in one pass |
| [scripts/restore.sh](scripts/restore.sh) | Ubuntu, after setup + reboot | Unpacks the backup and leaves out the Fedora/KDE bits that would fight Ubuntu |
**GeForce NOW on the 32:9 monitor:** the service only streams 16:9 (and some 21:9) aspect
ratios, so expect pillarboxing at 5120x1440 — that's a service limitation, not a config problem.
All three are safe to re-run.
## Development setup
## Why leave Bazzite
All 29 projects in `~/Documents/Development` were scanned for toolchain needs — the full
mapping (what the image covers, mise versions, the native-build distrobox, hardware udev rules)
is in [DEVELOPMENT.md](DEVELOPMENT.md). After first boot, run
[scripts/dev-setup.sh](scripts/dev-setup.sh) to set it all up.
The read-only base made ordinary things into projects. VS Code had to be layered with rpm-ostree,
Node and .NET came from Homebrew, and Claude desktop ran inside an Ubuntu distrobox because it
only ships for Ubuntu/Debian. On Ubuntu, each of those is an `apt install`.
## Post-install steps (manual by design — don't automate these)
## The move, in order
### PIA VPN — native WireGuard, not the app
### 1. Back up (on Bazzite)
The PIA desktop app installs into `/usr` and breaks on the read-only filesystem. Skip it:
1. Log in at PIA's [OpenVPN config generator](https://www.privateinternetaccess.com/pages/ovpn-config-generator)
(or generate a WireGuard config via their API/support flow) and download configs for your
preferred regions.
2. KDE **System Settings → Network → Connections → Add (+) → Import VPN connection…**, pick the
downloaded file, and enter your PIA credentials.
3. This is plain NetworkManager — it lives in `/etc`, survives every update, and gets a
connect/disconnect toggle in the system tray.
### Microsoft Teams — PWA via Edge
No native Linux client. Install **Microsoft Edge specifically** (corporate conditional
access / Intune compatibility): `flatpak install flathub com.microsoft.Edge`, sign in to
<https://teams.microsoft.com>, then menu → **Apps → Install this site as an app**.
### Claude desktop — PWA
No official Linux app. Open <https://claude.ai> in Edge (or any Chromium browser) and use
**Install this site as an app** the same way.
### AnythingLLM — not on Flathub
No Flatpak exists (verified 2026-07: zero Flathub search hits). Options: the official desktop
AppImage from [anythingllm.com](https://anythingllm.com/desktop) (works fine on atomic distros —
keep it in `~/Apps` and use e.g. Gear Lever to integrate it), or run the server edition in a
container: `docker run -p 3001:3001 mintplexlabs/anythingllm` (add
`--device nvidia.com/gpu=all` for 5090 acceleration).
### Proton-GE
Already handled by Bazzite: use the preinstalled **ProtonUp-Qt** GUI (or browse `ujust` — run
`ujust` with no arguments to list recipes). Don't install anything extra.
## Node / toolchains — not in the image
Node, npm, etc. are deliberately **not layered**. Use per-project versions instead:
From this repo's folder:
```bash
brew install mise # Homebrew ships in the base image
mise use node@22 # per-project .mise.toml; `mise use -g node@22` for a global default
scripts/backup.sh # -> /var/mnt/server-marvin-personal/desktop-backup-<date>/
```
(Plain `brew install node` also works if you just want one global Node.)
Close Steam, Firefox, Discord and Claude first. The script refuses to write to the disk that's about
to be wiped. It's about **360 GB**, so expect around 2 hours on gigabit, including the read-back
check.
## Display note
| Archive | Size | Contents |
|---|---|---|
| `home.tar.zst` | ~120 GB | Everything in `~` except caches, Trash, container images, and the three below. Includes `Desktop/Becky_backup` (63 GB), all Flatpak app data (`~/.var/app`), Lutris games in `~/Games` |
| `xlcore.tar.zst` | ~124 GB | FFXIV install, because Square's patch servers make a redownload take hours |
| `comfyui.tar.zst` | ~115 GB | ComfyUI minus its venv (mostly models) |
| `steam-saves.tar.zst` | ~5 GB | Steam `userdata` + Proton `compatdata` (saves and prefixes). Game files on the home drive (216 GB) redownload |
The 49" 32:9 (5120x1440) ultrawide needs nothing at the image layer. Post-install: set scale and
refresh rate in KDE System Settings → Display; KDE's window tiling (Meta+drag, or a tiler like
Polonium) is worth setting up at this width. VMs are remote via Remmina — there is intentionally
no local hypervisor tooling in this image.
`manifest/` holds reference lists: Flatpaks, VS Code extensions, Steam games, the old fstab and
Flatpak overrides. The archives contain `~/.ssh` and `~/.gnupg`, so the backup folder is as
sensitive as your home folder.
**Not backed up, but not lost:** the Data drive (SteamLibrary, `emu/`) isn't touched by the
install.
### 2. Install Ubuntu 26.04 LTS
> **⚠ Two identical 1 TB NVMe drives.** Install to the **Samsung SSD 990 PRO**. Never pick the
> **Samsung SSD 970 EVO Plus**: that's the "Data" drive with your second Steam library, and it's
> full, so nothing on it is backed up. `nvme0`/`nvme1` can swap between boots, so go by the model
> name the installer shows.
- "Erase disk and install Ubuntu" on the 990 PRO is fine.
- Make the user **`ckoch`**. The first user gets UID 1000, which matches the file ownership on the
Data drive.
- The "third-party drivers" checkbox doesn't matter; setup.sh installs the NVIDIA driver either way.
- Prefer KDE? Install **Kubuntu 26.04** instead. The scripts don't care which desktop you run.
### 3. Set up
```bash
sudo apt install -y git
git clone https://git.lazypugs.com/ckoch/main-desktop.git ~/Documents/git/main-desktop
cd ~/Documents/git/main-desktop && scripts/setup.sh
```
It asks for your sudo password once and for the server-marvin SMB username/password (stored
root-only in `/etc/samba/credentials-marvin`). Flatpaks take most of the time. **Reboot** when it
finishes.
What it installs:
- **NVIDIA driver:** Ubuntu's recommended **open-kernel** branch (595 today) with the prebuilt,
signed modules, so there's no DKMS. The script stops if it ends up with a non-open driver,
because Blackwell only runs on the open modules. It also installs 32-bit GL for Steam/Proton.
- **Gaming:** Steam (`steam-installer` + `steam-devices` for controllers), Lutris, GameMode,
MangoHud. ProtonUp-Qt (Flatpak) for Proton-GE. The Xbox Bluetooth fix (`disable_ertm`)
carries over from Bazzite.
- **Containers:** Docker CE from Docker's repo (not the snap), with `--gpus all` wired up through
nvidia-container-toolkit. Also Podman. You're added to `docker` and `kvm`.
- **Apps:** the 27 Flatpaks in [flatpaks.txt](flatpaks.txt), picked from the 42 that were on
Bazzite (the rest are listed at the bottom of that file, commented out, if you want one back).
Ubuntu's Firefox snap is removed in favor of the Flatpak so your old profile restores
untouched. A systemd timer updates Flatpaks daily.
- **Dev:** VS Code, Claude desktop (Anthropic's apt repo), Claude Code CLI, and
`build-essential`/cmake, so no dev distrobox needed. **No Node or .NET on the host** by
choice — LudosData and landingPage build in Docker. To add them later:
`sudo add-apt-repository ppa:jdxcode/mise && sudo apt install mise dotnet-sdk-10.0`.
- **Mounts:** the Data drive at **`/mnt/data`** and server-marvin at
**`/mnt/server-marvin-personal`** (automounted).
### 4. Restore
```bash
scripts/restore.sh # newest backup on server-marvin
ONLY="home steam-saves" scripts/restore.sh # skip the big ones if you want
```
First it pulls the Flatpak NVIDIA GL runtime (only possible once the driver is loaded after the
reboot). Without it, Flatpak apps render in software.
It deliberately skips a few things:
- Fedora's `.bashrc` and `.bash_profile`
- `~/.local/bin` (old pip `--user` tools such as rembg)
- KDE's GNOME-settings database and generated GTK themes
- Bazzite's autostart items
Everything is still in the archives, e.g.
`tar -I zstd -xf home.tar.zst -C ~ ./.config/some-app`.
Old configs full of `/var/home/ckoch/...` paths keep working through a `/var/home → /home`
symlink. Claude Code's per-project memory and history are renamed to the new paths.
## Check it worked
```bash
nvidia-smi # RTX 5090, driver 595.x
modinfo -F license nvidia # "Dual MIT/GPL" = open modules
docker run --rm --gpus all nvidia/cuda:12.8.0-base-ubuntu24.04 nvidia-smi
flatpak list --app | wc -l # 27
findmnt /mnt/data /mnt/server-marvin-personal
claude --version && code --version
```
Podman's `--device nvidia.com/gpu=all` needs a CDI spec. The toolkit normally generates one at
boot; if Podman says the device is unknown, run
`sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml`.
## By hand after restore
- **Steam:** sign in, then **Settings → Storage → Add drive → `/mnt/data/SteamLibrary`**. Games
there come back without downloading.
- **ComfyUI:** `cd ~/ComfyUI && python3 -m venv venv && venv/bin/pip install -r requirements.txt`
(plus any custom nodes' requirements).
- **Display:** Settings → Displays. The monitor does **5120x1440 @ 240 Hz**; Bazzite was running it
at 120. Turn on variable refresh rate there too. Ubuntu's built-in window tiling works well at
this width.
- **PIA VPN:** the official Linux app works on Ubuntu (it couldn't on Bazzite's read-only
`/usr`). Download the installer from PIA and run it.
## Coming from Bazzite: what moved
| Was | Now |
|---|---|
| `rpm-ostree` layering / `ujust` | `apt` |
| Homebrew `node@24`, `dotnet` | Nothing on the host — those projects build in Docker |
| Claude desktop in a distrobox | Native `claude-desktop` package |
| `/var/home/ckoch` | `/home/ckoch` (+ compatibility symlink) |
| `/run/media/ckoch/Data` | `/mnt/data` |
| `/var/mnt/server-marvin-personal` | `/mnt/server-marvin-personal` |
| Image updates + rollback | Software Updater / `sudo apt update && sudo apt upgrade`; Flatpaks auto-update daily. No rollback by default; `sudo apt install timeshift` if you miss it |
## Maintenance
- **Change the Flatpak list / packages:** edit [recipes/recipe.yml](recipes/recipe.yml), commit,
and re-run `bluebuild switch`. (Removing an app from the list does not uninstall it from the
machine; `flatpak uninstall` it once by hand.)
- **Jump Fedora majors:** change `image-version: stable-44` → `stable-45` in the recipe when
ready, then `bluebuild switch`.
- **Reclaim build disk:** `bluebuild prune` cleans build caches.
- **Publishing later:** if this ever moves to a registry + signed rebases, the pieces are in
place — `cosign.pub` is committed, the signing module is in the recipe, and
`bluebuild build --push --registry <host> --registry-namespace <ns>` does the rest (note:
as of CLI v0.9.36, local `build` runs bake the signing policy for `localhost` — a registry
push should be done from CI or with a `bluebuild generate`-based two-step).
- **Add or remove an app:** edit [flatpaks.txt](flatpaks.txt) and re-run `scripts/setup.sh`.
Removing a line doesn't uninstall anything; `flatpak uninstall <id>` once by hand.
- **Next LTS (28.04):** Ubuntu offers the upgrade in Software Updater once 28.04.1 is out. The
upgrader disables third-party repos (Docker, NVIDIA toolkit, etc.). Turn them back on in
Software & Updates → Other Software afterwards.