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:
@@ -1,3 +0,0 @@
|
||||
# NEVER commit the cosign private key — it goes in the SIGNING_SECRET GitHub secret.
|
||||
cosign.key
|
||||
/.bluebuild-scripts_*
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
# Development setup
|
||||
|
||||
What the 29 projects in `~/Documents/Development` need on the new machine, mapped against what
|
||||
the image already provides. Scanned 2026-07 from each project's manifests, Dockerfiles, and
|
||||
setup scripts. Run [scripts/dev-setup.sh](scripts/dev-setup.sh) after first boot to install the
|
||||
toolchain layer in one shot.
|
||||
|
||||
## Already in the image — install nothing
|
||||
|
||||
| Need | Provided by | Used by |
|
||||
|---|---|---|
|
||||
| Docker CE + compose, Podman, skopeo | Bazzite DX base | requester, serverTransfer, ispy, keyWord, encoderPro, movieScanner, kidstube, Docker-Registry-Browser, smartSpeaker… |
|
||||
| NVIDIA driver (610+ open) + container toolkit + CDI | base (nvidia image) | AIVoice, ispy, keyWord, encoderPro, smartSpeaker whisper, superBossMonster art pipeline |
|
||||
| qemu/KVM + virt tooling | DX base | BC250 VM testing |
|
||||
| android-tools (adb) | DX base | denon-bluetooth btsnoop captures |
|
||||
| Steam + Proton | Bazzite base | BG4_UltraWide_fix |
|
||||
| VS Code, git, Homebrew, just | DX base | everything |
|
||||
| BlueZ/bluetoothd, rsync, openssh | Fedora base | denon-bluetooth, serverTransfer |
|
||||
| Godot (current stable), Android Studio | first-boot Flatpaks | superBossMonster/FFCardGame, native-angular |
|
||||
|
||||
AIVoice's `SETUP.md` warning about snap Docker / Docker Desktop is moot here: Bazzite DX ships
|
||||
native docker-ce, which is exactly what it wants. Its
|
||||
`nvidia-ctk runtime configure --runtime=docker` step is the same one in the README's GPU
|
||||
section.
|
||||
|
||||
## Toolchains — mise (all per-version needs found in the scan)
|
||||
|
||||
No project commits `.nvmrc`/`.python-version`/`.tool-versions`, so versions below come from
|
||||
Dockerfiles and build configs. `dev-setup.sh` installs mise and these versions:
|
||||
|
||||
- **Node 22** global default (requester, movieScanner); projects also reference 18
|
||||
(Docker-Registry-Browser, movieMetaTagger) and 20 (FFCardGame server, kidstube) — installed
|
||||
side-by-side, pin per-project with `mise use node@18` in the project dir (writes
|
||||
`mise.toml` — worth committing so this guesswork ends).
|
||||
- **Python 3.12** (serverTransfer, movieMetaTagger, movieScanner, nameFixer4000, BG4 tools) and
|
||||
**3.11** floor (FRScanner). Fedora 44's system python is newer — don't rely on it for these.
|
||||
- **JDK 21** (villager-logic-fix, Minecraft-Village-Warriors release target), **JDK 17**
|
||||
(native-angular android-host), **JDK 25** (Minecraft-Village-Warriors toolchain — Gradle can
|
||||
also auto-provision it). Gradle itself always via each repo's wrapper.
|
||||
- **.NET 10 SDK** (requester; also rebuilds the BG4 BepInEx plugin).
|
||||
- **Go** and **Rust** — present on the old machine (`~/go`, `~/.cargo`); no scanned project pins
|
||||
a version, so latest.
|
||||
|
||||
## Native builds — use a distrobox, not the host
|
||||
|
||||
Compilers and C library headers don't belong on the atomic host. One Fedora distrobox covers
|
||||
every native-build need the scan found:
|
||||
|
||||
```bash
|
||||
distrobox create dev --image registry.fedoraproject.org/fedora:44
|
||||
distrobox enter dev
|
||||
sudo dnf install -y gcc gcc-c++ make cmake pkgconf python3-devel \
|
||||
gtk4-devel \ # native-angular linux-host (CMake + GTK4)
|
||||
zbar \ # DVDPhotoMatic (pyzbar)
|
||||
libsndfile mediainfo-libs \ # FRScanner audio / movieMetaTagger pymediainfo
|
||||
rtl-sdr rtl-sdr-devel \ # FRScanner RTL-SDR + Cython builds
|
||||
mkvtoolnix ffmpeg-free \ # movieMetaTagger mkvpropedit; ffmpeg fallback
|
||||
python3-tkinter python3-dbus \ # denon-bluetooth GUI
|
||||
flashrom # BC250 BIOS flashing (CH347 over USB)
|
||||
```
|
||||
|
||||
Native node modules (kidstube's better-sqlite3, FFCardGame's bcrypt) build fine inside the
|
||||
distrobox too — or in their Docker builds, which is how they ship anyway.
|
||||
|
||||
## Host-level odds and ends
|
||||
|
||||
- **RTL-SDR udev rules** (FRScanner): the dongle permissions must live on the *host* —
|
||||
`dev-setup.sh` writes `/etc/udev/rules.d/20-rtlsdr.rules` (`/etc` is mutable and survives
|
||||
updates).
|
||||
- **ffmpeg on the host** (audioSticher, AIVoice host venv, encoderPro host runs): Bazzite
|
||||
ships ffmpeg — verify with `ffmpeg -version`; if a project needs full codecs beyond it,
|
||||
`brew install ffmpeg` wins on PATH.
|
||||
- **Pinned Godot binaries**: superBossMonster hardcodes `~/godot/Godot_v4.3-stable_linux.x86_64`
|
||||
and FFCardGame ships its own 4.2 binary. Keep the `~/godot/` directory from the old machine
|
||||
(plus export templates in `~/.local/share/godot/`); the Flatpak is only "current stable".
|
||||
- **gh CLI**: `brew install gh` (in `dev-setup.sh`) — used across projects.
|
||||
- **BlueBuild CLI** (BC250 and this repo): the installer one-liner in the README.
|
||||
- **API keys**: ANTHROPIC_API_KEY (FFCardGame tools, DVDPhotoMatic), TMDB/OMDB
|
||||
(movieScanner, nameFixer4000, DVDPhotoMatic), YouTube Data v3 (kidstube) — carried in
|
||||
per-project `.env` files; bring them over with your home directory.
|
||||
|
||||
## Per-project cheat sheet
|
||||
|
||||
| Project | Needs beyond the image |
|
||||
|---|---|
|
||||
| AIVoice | py3.12 venv (mise), ffmpeg, GPU containers (covered) |
|
||||
| AIVoices_HA, personal-landing, cardscraper, audioSticher, teamsTrick | nothing / stdlib python |
|
||||
| BC250 | BlueBuild CLI, flashrom (distrobox) |
|
||||
| BG4_UltraWide_fix | .NET 10, py3.12, Steam/Proton (covered) |
|
||||
| denon-bluetooth | distrobox tkinter+dbus, adb (covered) |
|
||||
| Docker-Registry-Browser | Node 18 |
|
||||
| DVDPhotoMatic | finished project — py3.12, zbar (distrobox), webcam if ever revived; OpenSCAD flatpak for the jig files if needed |
|
||||
| encoderPro, ispy, keyWord, movieScanner, nameFixer4000, serverTransfer | Docker (+GPU) only |
|
||||
| FFCardGame | Godot 4.2 binary, Node 20, python+anthropic |
|
||||
| FRScanner | py3.11+, rtl-sdr distrobox + host udev rules |
|
||||
| kidstube | Node 20 (+distrobox for native build outside Docker) |
|
||||
| Minecraft mods (×2) | JDK 21/25 via mise |
|
||||
| movieMetaTagger | py3.12, Node 18, mkvtoolnix/mediainfo (distrobox) |
|
||||
| native-angular | Node 20+, JDK 17, Android Studio (flatpak), GTK4 distrobox |
|
||||
| requester | .NET 10, Node 22, postgres via compose |
|
||||
| smartSpeaker | Docker (covered); Pi hardware is remote |
|
||||
| superBossMonster | Godot 4.3 binary + templates, ComfyUI in GPU container |
|
||||
| teamsTrick | X11 session note: KDE on Bazzite defaults to Wayland — run it in an X11 session or expect XTest to fail |
|
||||
@@ -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.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEog2z/j7tTbopTjEMcisWFqbIQ/cD
|
||||
WZP/m/ImJktzg0YZtSexbJxd1ltayXtl/6c3roGuPT+w1qZ+PuooR2BKkA==
|
||||
-----END PUBLIC KEY-----
|
||||
@@ -0,0 +1,66 @@
|
||||
# Flathub apps installed system-wide by scripts/setup.sh — one ID per line, '#' comments.
|
||||
# Picked from everything installed on the Bazzite box (scanned 2026-09-21, chosen 2026-09-23).
|
||||
# Restoring ~/.var/app brings each app's data back with it (logins, profiles, saves).
|
||||
# Removing a line doesn't uninstall the app; `flatpak uninstall <id>` it once by hand.
|
||||
|
||||
# -- Internet / chat --
|
||||
org.mozilla.firefox # replaces Ubuntu's Firefox snap (setup.sh removes the snap)
|
||||
org.mozilla.thunderbird_esr
|
||||
com.discordapp.Discord
|
||||
com.bitwarden.desktop
|
||||
|
||||
# -- Media --
|
||||
com.spotify.Client # community packaging of the proprietary client
|
||||
org.videolan.VLC
|
||||
fr.handbrake.ghb
|
||||
com.makemkv.MakeMKV # community packaging
|
||||
|
||||
# -- Create / CAD / print --
|
||||
org.gimp.GIMP
|
||||
org.freecad.FreeCAD
|
||||
com.bambulab.BambuStudio
|
||||
org.godotengine.Godot # DCC projects target 4.7; Flathub tracks current stable
|
||||
org.mapeditor.Tiled
|
||||
|
||||
# -- Dev --
|
||||
com.sublimemerge.App
|
||||
|
||||
# -- Games / launchers --
|
||||
com.heroicgameslauncher.hgl
|
||||
net.davidotek.pupgui2 # ProtonUp-Qt — Proton-GE for Steam/Lutris (Bazzite shipped this)
|
||||
dev.goats.xivlauncher # FFXIV; game files live in ~/.xlcore
|
||||
com.moonlight_stream.Moonlight
|
||||
io.github.hmlendea.geforcenow-electron # unofficial GeForce NOW wrapper; 16:9/21:9 only
|
||||
org.prismlauncher.PrismLauncher
|
||||
com.unfolding_machines.pum_lite # PUM Lite — solo RPG / story tool
|
||||
|
||||
# -- Emulators --
|
||||
net.pcsx2.PCSX2 # official PCSX2 build — do not swap for community forks
|
||||
org.DolphinEmu.dolphin-emu
|
||||
app.xemu.xemu
|
||||
io.mgba.mGBA
|
||||
com.snes9x.Snes9x
|
||||
|
||||
# -- Games --
|
||||
net.wz2100.wz2100
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Installed on Bazzite, deliberately left behind. Uncomment to bring one back —
|
||||
# its data is still in the backup's home.tar.zst under .var/app/<id>.
|
||||
#
|
||||
# com.github.IsmaelMartinez.teams_for_linux # Teams
|
||||
# com.ktechpit.whatsie # WhatsApp
|
||||
# org.signal.Signal
|
||||
# io.github.hakuneko.HakuNeko
|
||||
# net.mkiol.SpeechNote # 3.9 GB of speech models
|
||||
# org.blender.Blender
|
||||
# org.libreoffice.LibreOffice # Ubuntu ships LibreOffice as debs anyway
|
||||
# org.photoqt.PhotoQt
|
||||
# com.google.AndroidStudio
|
||||
# io.github.shiftey.Desktop # GitHub Desktop
|
||||
# io.podman_desktop.PodmanDesktop
|
||||
# org.gnome.Calculator # Ubuntu has its own
|
||||
# net.longturn.freeciv21
|
||||
# org.develz.Crawl # ~/.crawl saves are still in the backup
|
||||
# net.sourceforge.atanks
|
||||
# org.frozen_bubble.frozen-bubble
|
||||
@@ -1,56 +0,0 @@
|
||||
# yaml-language-server: $schema=https://schema.blue-build.org/recipe-v1.json
|
||||
# Custom desktop image: Bazzite DX (KDE) + NVIDIA open kernel modules (RTX 5090 / Blackwell).
|
||||
#
|
||||
# The base image already ships: Steam + gaming stack, Flatpak, Docker CE + CLI,
|
||||
# Podman, VS Code, Homebrew, the NVIDIA 610+ open driver, AND nvidia-container-toolkit
|
||||
# with automatic CDI generation (ublue-nvctk-cdi.service). Nothing GPU/dev-related
|
||||
# needs to be layered here — this recipe only adds signature-verification policy
|
||||
# and the declarative first-boot Flatpak set.
|
||||
name: main-desktop
|
||||
description: Bazzite DX NVIDIA (open, KDE) with my first-boot Flatpak set, signed.
|
||||
|
||||
base-image: ghcr.io/ublue-os/bazzite-dx-nvidia
|
||||
# Pinned to Fedora 44. Bump deliberately (stable-45, ...) when ready to jump majors.
|
||||
image-version: stable-44
|
||||
# Deterministic tags regardless of where the build runs (local builds would
|
||||
# otherwise only produce a local-<version> tag; rebase refs rely on :latest).
|
||||
alt-tags:
|
||||
- latest
|
||||
- stable-44
|
||||
|
||||
modules:
|
||||
- type: default-flatpaks
|
||||
configurations:
|
||||
- scope: system
|
||||
notify: true
|
||||
install:
|
||||
# -- Official / verified Flathub listings --
|
||||
- com.bambulab.BambuStudio
|
||||
- com.bitwarden.desktop
|
||||
- com.discordapp.Discord
|
||||
- com.moonlight_stream.Moonlight
|
||||
- dev.goats.xivlauncher
|
||||
- fr.handbrake.ghb
|
||||
- io.podman_desktop.PodmanDesktop
|
||||
- net.pcsx2.PCSX2 # official PCSX2 build — do not swap for community forks
|
||||
- net.wz2100.wz2100
|
||||
- org.freecad.FreeCAD # current ID; old org.freecadweb.FreeCAD is dead
|
||||
- org.gimp.GIMP
|
||||
- org.libreoffice.LibreOffice
|
||||
- org.mozilla.Thunderbird
|
||||
- org.mozilla.firefox
|
||||
- org.prismlauncher.PrismLauncher
|
||||
# -- Community / unverified listings (see README) --
|
||||
- com.google.AndroidStudio # native-angular android-host (SDK 35 + NDK)
|
||||
- com.makemkv.MakeMKV
|
||||
- com.spotify.Client
|
||||
- io.github.hmlendea.geforcenow-electron # unofficial wrapper; 16:9/21:9 streams only
|
||||
- io.github.shiftey.Desktop # GitHub Desktop Linux fork; "shiftey" is correct
|
||||
- org.godotengine.Godot
|
||||
- org.videolan.VLC
|
||||
|
||||
# Installs the cosign public key + container signature-verification policy.
|
||||
# Unused in the current build-locally model (`bluebuild switch` rebases from a
|
||||
# local oci-archive, unsigned) — kept so publishing to a registry later only
|
||||
# requires a push, not a recipe change. Requires cosign.pub at repo root.
|
||||
- type: signing
|
||||
Executable
+111
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run on the Bazzite box BEFORE wiping it. Archives your home folder to server-marvin as
|
||||
# tar.zst files, then reads every archive back to prove it's intact.
|
||||
#
|
||||
# scripts/backup.sh # -> /var/mnt/server-marvin-personal/desktop-backup-<date>
|
||||
# scripts/backup.sh /some/other/dir # any directory NOT on the disk you're about to wipe
|
||||
#
|
||||
# Re-running skips archives that already finished (FORCE=1 redoes them).
|
||||
# Close Steam, Firefox, Discord and Claude first — files that change mid-read get a warning.
|
||||
set -euo pipefail
|
||||
|
||||
SHARE=/var/mnt/server-marvin-personal
|
||||
DEST="${1:-$SHARE/desktop-backup-$(date +%F)}"
|
||||
|
||||
step() { printf '\n\033[1;34m== %s ==\033[0m\n' "$*"; }
|
||||
warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; }
|
||||
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
# -- Make sure the backup lands somewhere that survives the reinstall ------------------
|
||||
if [[ $DEST == "$SHARE"/* ]]; then
|
||||
ls "$SHARE" >/dev/null 2>&1 || true # poke the automount
|
||||
mountpoint -q "$SHARE" || die "$SHARE isn't mounted — is server-marvin up?"
|
||||
fi
|
||||
mkdir -p "$DEST"
|
||||
disk_of() { findmnt -n -o SOURCE -T "$1" | sed 's/\[.*//'; }
|
||||
[[ $(disk_of "$DEST") != "$(disk_of "$HOME")" ]] \
|
||||
|| die "$DEST is on the same disk as your home — that's the disk Ubuntu will wipe"
|
||||
echo "Backing up $HOME -> $DEST"
|
||||
df -h "$DEST" | tail -1
|
||||
|
||||
# -- Archive helper ---------------------------------------------------------------------
|
||||
archive() { # archive <name> <tar args + members...>
|
||||
local name=$1 out="$DEST/$1.tar.zst"; shift
|
||||
step "$name"
|
||||
if [[ -s $out && -z ${FORCE:-} ]]; then echo "already done, skipping"; return; fi
|
||||
local rc=0
|
||||
tar --create --file="$out.partial" --use-compress-program='zstd -T0 -3' \
|
||||
--directory="$HOME" \
|
||||
--checkpoint=100000 --checkpoint-action='ttyout= %{%H:%M:%S}t %T%*\r' \
|
||||
"$@" || rc=$?
|
||||
echo
|
||||
# GNU tar exits 1 when a file changed while it was being read (an app was still open).
|
||||
# Everything else still made it in, so keep the archive; anything above 1 is fatal.
|
||||
(( rc <= 1 )) || die "tar failed on $name (exit $rc)"
|
||||
(( rc == 0 )) || warn "$name: some files changed while being read — close apps and FORCE=1 to redo if it matters"
|
||||
mv "$out.partial" "$out"
|
||||
ls -lh "$out" | awk '{print " " $5}'
|
||||
}
|
||||
|
||||
# Big, self-contained folders get their own archive so they can be restored (or skipped)
|
||||
# separately. Everything else goes in home.tar.zst.
|
||||
archive home \
|
||||
--anchored \
|
||||
--exclude=./.cache \
|
||||
--exclude=./.local/share/Trash \
|
||||
--exclude=./.local/share/Steam \
|
||||
--exclude=./.steam \
|
||||
--exclude=./.local/share/containers \
|
||||
--exclude=./.local/share/flatpak \
|
||||
--exclude=./.local/share/baloo \
|
||||
--exclude='./.var/app/*/cache' \
|
||||
--exclude='./.claude.json.tmp.*' \
|
||||
--exclude=./.xlcore \
|
||||
--exclude=./ComfyUI \
|
||||
--no-anchored \
|
||||
--exclude=node_modules \
|
||||
--exclude=__pycache__ \
|
||||
.
|
||||
|
||||
# FFXIV game install (~124 GB) — Square's patch servers make a redownload take hours.
|
||||
[[ -d $HOME/.xlcore ]] && archive xlcore ./.xlcore
|
||||
|
||||
# ComfyUI minus its venv (rebuilt on Ubuntu); this is ~110 GB of models.
|
||||
[[ -d $HOME/ComfyUI ]] && archive comfyui \
|
||||
--anchored --exclude=./ComfyUI/venv --exclude=./ComfyUI/.venv \
|
||||
--no-anchored --exclude=__pycache__ \
|
||||
./ComfyUI
|
||||
|
||||
# Steam: only saves and settings. Games get redownloaded; the ones on the Data drive stay put.
|
||||
archive steam-saves \
|
||||
./.local/share/Steam/userdata \
|
||||
./.local/share/Steam/steamapps/compatdata
|
||||
|
||||
# -- Reference info for the rebuild -------------------------------------------------------
|
||||
step "manifest"
|
||||
m="$DEST/manifest"; mkdir -p "$m"
|
||||
flatpak list --app --columns=application,origin,installation > "$m/flatpaks.txt" 2>/dev/null || true
|
||||
code --list-extensions > "$m/vscode-extensions.txt" 2>/dev/null || true
|
||||
brew leaves > "$m/brew-leaves.txt" 2>/dev/null || true
|
||||
rpm-ostree status > "$m/rpm-ostree-status.txt" 2>/dev/null || true
|
||||
cp /etc/fstab "$m/fstab"
|
||||
cp -r "$HOME/.local/share/flatpak/overrides" "$m/flatpak-overrides-user" 2>/dev/null || true
|
||||
cp -r /var/lib/flatpak/overrides "$m/flatpak-overrides-system" 2>/dev/null || true
|
||||
lsblk -f > "$m/disks.txt"
|
||||
ls "$HOME/.local/share/Steam/steamapps/common" > "$m/steam-games-home.txt" 2>/dev/null || true
|
||||
ls /run/media/"$USER"/Data/SteamLibrary/steamapps/common > "$m/steam-games-data-drive.txt" 2>/dev/null || true
|
||||
ls "$m"
|
||||
|
||||
# -- Prove every archive reads back cleanly ----------------------------------------------
|
||||
if [[ -z ${SKIP_VERIFY:-} ]]; then
|
||||
for f in "$DEST"/*.tar.zst; do
|
||||
step "verify $(basename "$f")"
|
||||
tar --use-compress-program=zstd --list --file="$f" > /dev/null || die "$f is corrupt — rerun with FORCE=1"
|
||||
echo "ok"
|
||||
done
|
||||
fi
|
||||
|
||||
step "done"
|
||||
du -sh "$DEST"/*.tar.zst
|
||||
echo "Backup is in: $DEST"
|
||||
echo "Safe to wipe the 990 PRO once you've eyeballed that list."
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# One-shot developer toolchain setup for the main-desktop image.
|
||||
# Run as your user after first boot (safe to re-run). See DEVELOPMENT.md for the why.
|
||||
set -euo pipefail
|
||||
|
||||
echo "== Homebrew tools =="
|
||||
brew install mise gh cosign
|
||||
|
||||
echo "== Toolchains (mise) =="
|
||||
mise use -g node@22 python@3.12 go@latest rust@latest java@temurin-21 dotnet@10
|
||||
mise install node@18 node@20 python@3.11 java@temurin-17 java@temurin-25
|
||||
|
||||
echo "== RTL-SDR udev rules (FRScanner) =="
|
||||
if [ ! -f /etc/udev/rules.d/20-rtlsdr.rules ]; then
|
||||
sudo tee /etc/udev/rules.d/20-rtlsdr.rules >/dev/null <<'EOF'
|
||||
# Generic RTL2832U (rtl-sdr) dongles — allow non-root access
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", MODE="0666", TAG+="uaccess"
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2832", MODE="0666", TAG+="uaccess"
|
||||
EOF
|
||||
sudo udevadm control --reload-rules
|
||||
fi
|
||||
|
||||
echo "== Native-build distrobox =="
|
||||
if ! distrobox list 2>/dev/null | grep -q '^.*| *dev '; then
|
||||
distrobox create --yes dev --image registry.fedoraproject.org/fedora:44
|
||||
distrobox enter dev -- sudo dnf install -y gcc gcc-c++ make cmake pkgconf python3-devel \
|
||||
gtk4-devel zbar libsndfile mediainfo-libs rtl-sdr rtl-sdr-devel mkvtoolnix \
|
||||
python3-tkinter python3-dbus flashrom
|
||||
fi
|
||||
|
||||
echo "== Done =="
|
||||
echo "Remaining manual bits (see DEVELOPMENT.md):"
|
||||
echo " - copy ~/godot/ (pinned 4.2/4.3 binaries) and ~/.local/share/godot/ from old machine"
|
||||
echo " - bring per-project .env files (API keys)"
|
||||
echo " - ujust dx-group (docker group), then re-login"
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
# Run on Ubuntu AFTER setup.sh and a reboot. Unpacks the Bazzite backup into your home,
|
||||
# minus the Fedora/KDE-specific bits that would fight a fresh Ubuntu desktop.
|
||||
#
|
||||
# scripts/restore.sh # newest desktop-backup-* on server-marvin
|
||||
# scripts/restore.sh /path/to/backup # a specific backup folder
|
||||
# ONLY="home steam-saves" scripts/restore.sh # just some archives
|
||||
#
|
||||
# Everything is still in the archives, so anything skipped here can be pulled out later:
|
||||
# tar -I zstd -xf home.tar.zst -C ~ ./.config/some-app
|
||||
set -euo pipefail
|
||||
|
||||
SHARE=/mnt/server-marvin-personal
|
||||
|
||||
step() { printf '\n\033[1;34m== %s ==\033[0m\n' "$*"; }
|
||||
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
|
||||
[[ $EUID -ne 0 ]] || die "run as your user, not with sudo"
|
||||
ls "$SHARE" >/dev/null 2>&1 || true # poke the automount
|
||||
SRC="${1:-$(ls -d "$SHARE"/desktop-backup-* 2>/dev/null | sort | tail -1)}"
|
||||
[[ -n $SRC && -d $SRC ]] || die "no backup found — pass the folder, or check $SHARE is mounted"
|
||||
ONLY=${ONLY:-home steam-saves xlcore comfyui}
|
||||
echo "Restoring from $SRC: $ONLY"
|
||||
|
||||
pgrep -x steam >/dev/null && die "quit Steam first"
|
||||
|
||||
# Flatpak apps need a GL extension matching the NVIDIA driver, and flatpak can only see the
|
||||
# driver version once it's loaded — i.e. now, after the reboot. Without this they render in software.
|
||||
step "Flatpak NVIDIA runtime"
|
||||
sudo flatpak update -y --noninteractive
|
||||
|
||||
# Old configs (Heroic, Lutris, XIVLauncher, Proton prefixes, ...) hold absolute
|
||||
# /var/home/ckoch/... paths. One symlink makes all of them resolve on Ubuntu.
|
||||
[[ -e /var/home ]] || sudo ln -s /home /var/home
|
||||
|
||||
# Left out of home.tar.zst on purpose:
|
||||
skip=(
|
||||
--anchored
|
||||
# Fedora's shell dotfiles; Ubuntu's are better and setup.sh already added what's needed
|
||||
--exclude=./.bashrc --exclude=./.bash_profile --exclude=./.bash_logout --exclude=./.profile
|
||||
--exclude=./.bashrc.d --exclude=./.zshrc --exclude=./.zprofile
|
||||
# .NET tool shims built against Homebrew's SDK (setup.sh reinstalled dotnet-ef)
|
||||
--exclude=./.dotnet
|
||||
# pip --user installs for Fedora's Python, and launchers for the old distroboxes
|
||||
--exclude=./.local/bin --exclude='./.local/lib/python*'
|
||||
--exclude='./.local/share/applications/claude*'
|
||||
--exclude='./.local/share/applications/war1gus*'
|
||||
--exclude=./.local/share/applications/code.desktop
|
||||
# desktop state that would clobber Ubuntu's: GNOME's settings db, keyring, default apps,
|
||||
# and KDE-generated GTK themes that would recolor GNOME apps
|
||||
--exclude=./.config/dconf --exclude=./.local/share/keyrings --exclude=./.config/mimeapps.list
|
||||
--exclude=./.config/gtk-3.0 --exclude=./.config/gtk-4.0
|
||||
--exclude=./.config/gtkrc --exclude=./.config/gtkrc-2.0 --exclude=./.gtkrc-2.0
|
||||
# Bazzite leftovers
|
||||
--exclude=./.config/bazzite --exclude=./.config/autostart/sb-key-notify.desktop
|
||||
# old SMB password file (setup.sh stored the credentials in /etc/samba)
|
||||
--exclude=./.smbcredentials
|
||||
)
|
||||
|
||||
for name in $ONLY; do
|
||||
f="$SRC/$name.tar.zst"
|
||||
step "$name"
|
||||
[[ -f $f ]] || { echo "not in backup, skipping"; continue; }
|
||||
args=()
|
||||
[[ $name == home ]] && args=("${skip[@]}")
|
||||
tar --extract --file="$f" --use-compress-program=zstd --directory="$HOME" \
|
||||
--checkpoint=100000 --checkpoint-action='ttyout= %{%H:%M:%S}t %T%*\r' \
|
||||
"${args[@]}"
|
||||
echo "ok"
|
||||
done
|
||||
|
||||
# Claude Code keys memory/history by folder path; /var/home/ckoch is now /home/ckoch.
|
||||
if [[ -d ~/.claude/projects ]]; then
|
||||
for d in ~/.claude/projects/-var-home-*; do
|
||||
[[ -d $d ]] || continue
|
||||
new=~/.claude/projects/${d##*/-var}
|
||||
[[ -e $new ]] || mv "$d" "$new"
|
||||
done
|
||||
[[ -f ~/.claude.json ]] && sed -i 's#"/var/home/#"/home/#g' ~/.claude.json
|
||||
fi
|
||||
|
||||
step "Done — a few things to finish by hand"
|
||||
cat <<EOF
|
||||
- Steam: open it, sign in, then Settings > Storage > Add drive > /mnt/data/SteamLibrary
|
||||
(games on the Data drive come back without downloading; home-drive games redownload).
|
||||
- ComfyUI: rebuild its venv —
|
||||
cd ~/ComfyUI && python3 -m venv venv && venv/bin/pip install -r requirements.txt
|
||||
- Old pip --user tools (rembg etc.) weren't restored: pipx install "rembg[cli]" if you want it.
|
||||
- Log out and back in so every app picks up the restored settings.
|
||||
EOF
|
||||
Executable
+226
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fresh Ubuntu 26.04 LTS -> my desktop, in one pass. Run as your user; it sudos as needed.
|
||||
# Safe to re-run: each step checks before it changes anything. Reboot when it finishes,
|
||||
# then run scripts/restore.sh.
|
||||
set -euo pipefail
|
||||
|
||||
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
# Second NVMe (970 EVO Plus, label "Data"): SteamLibrary + emulator files. Never formatted.
|
||||
DATA_UUID=29db4bd3-b743-417d-aa39-4da4acc9df3f
|
||||
DATA_MNT=/mnt/data
|
||||
# server-marvin SMB share — the backup lives here. \040 is fstab's escaped space.
|
||||
MARVIN_SHARE='//192.168.1.193/Personal\040Files\040-\040ckoch'
|
||||
MARVIN_MNT=/mnt/server-marvin-personal
|
||||
MARVIN_CREDS=/etc/samba/credentials-marvin
|
||||
|
||||
step() { printf '\n\033[1;34m== %s ==\033[0m\n' "$*"; }
|
||||
warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$*" >&2; }
|
||||
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
apt_install() { sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "$@"; }
|
||||
|
||||
[[ $EUID -ne 0 ]] || die "run as your user, not with sudo"
|
||||
. /etc/os-release
|
||||
[[ $ID == ubuntu ]] || die "expected Ubuntu, found $ID"
|
||||
[[ $VERSION_ID == 26.04 ]] || warn "written for 26.04; this is $VERSION_ID — carrying on"
|
||||
CODENAME=${UBUNTU_CODENAME:-$VERSION_CODENAME}
|
||||
|
||||
# Ask for the password once and keep sudo alive for the whole run (Flatpaks take a while).
|
||||
sudo -v
|
||||
while true; do sudo -n true; sleep 50; kill -0 "$$" 2>/dev/null || exit; done 2>/dev/null &
|
||||
keepalive=$!
|
||||
trap 'kill "$keepalive" 2>/dev/null' EXIT
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
step "Apt repositories"
|
||||
sudo apt-get update
|
||||
apt_install curl gpg ca-certificates software-properties-common
|
||||
for c in universe multiverse restricted; do sudo add-apt-repository -y -n "$c"; done
|
||||
sudo dpkg --add-architecture i386 # Steam + 32-bit game libraries
|
||||
sudo install -d -m 0755 /etc/apt/keyrings
|
||||
|
||||
# Docker CE — Docker's own repo, not the snap or Ubuntu's docker.io
|
||||
if [[ ! -f /etc/apt/sources.list.d/docker.sources ]]; then
|
||||
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||
sudo chmod a+r /etc/apt/keyrings/docker.asc
|
||||
sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
|
||||
Types: deb
|
||||
URIs: https://download.docker.com/linux/ubuntu
|
||||
Suites: $CODENAME
|
||||
Components: stable
|
||||
Signed-By: /etc/apt/keyrings/docker.asc
|
||||
EOF
|
||||
fi
|
||||
|
||||
# NVIDIA Container Toolkit — GPU access inside Docker/Podman
|
||||
if [[ ! -f /etc/apt/sources.list.d/nvidia-container-toolkit.list ]]; then
|
||||
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
|
||||
| sudo gpg --dearmor --yes -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
|
||||
curl -fsSL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
|
||||
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
|
||||
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list >/dev/null
|
||||
fi
|
||||
|
||||
# VS Code — Microsoft's repo (was rpm-ostree-layered on Bazzite)
|
||||
if [[ ! -f /etc/apt/sources.list.d/vscode.sources ]]; then
|
||||
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc \
|
||||
| sudo gpg --dearmor --yes -o /usr/share/keyrings/microsoft.gpg
|
||||
sudo tee /etc/apt/sources.list.d/vscode.sources >/dev/null <<EOF
|
||||
Types: deb
|
||||
URIs: https://packages.microsoft.com/repos/code
|
||||
Suites: stable
|
||||
Components: main
|
||||
Architectures: amd64
|
||||
Signed-By: /usr/share/keyrings/microsoft.gpg
|
||||
EOF
|
||||
fi
|
||||
|
||||
# GitHub CLI
|
||||
if [[ ! -f /etc/apt/sources.list.d/github-cli.list ]]; then
|
||||
sudo curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
||||
-o /etc/apt/keyrings/githubcli-archive-keyring.gpg
|
||||
sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
||||
| sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null
|
||||
fi
|
||||
|
||||
# Claude desktop — Anthropic's repo (ran inside an Ubuntu distrobox on Bazzite)
|
||||
if [[ ! -f /etc/apt/sources.list.d/claude-desktop.list ]]; then
|
||||
key=/usr/share/keyrings/claude-desktop-archive-keyring.asc
|
||||
sudo curl -fsSLo "$key" https://downloads.claude.ai/claude-desktop/key.asc
|
||||
if ! gpg --show-keys --with-colons "$key" 2>/dev/null \
|
||||
| grep -q '^fpr:::::::::31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE:'; then
|
||||
sudo rm -f "$key"; die "Claude desktop signing key has the wrong fingerprint — not adding the repo"
|
||||
fi
|
||||
echo "deb [arch=amd64,arm64 signed-by=$key] https://downloads.claude.ai/claude-desktop/apt/stable stable main" \
|
||||
| sudo tee /etc/apt/sources.list.d/claude-desktop.list >/dev/null
|
||||
fi
|
||||
|
||||
sudo apt-get update
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get full-upgrade -y
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
step "Packages"
|
||||
# steam-installer refuses to install non-interactively until its license is accepted.
|
||||
echo 'steam-installer steam/question select I AGREE' | sudo debconf-set-selections
|
||||
echo 'steam-installer steam/license note ' | sudo debconf-set-selections
|
||||
|
||||
apt_install \
|
||||
build-essential cmake pkg-config git git-lfs wget jq zstd unzip p7zip-full rsync htop \
|
||||
python3-venv python3-pip pipx ffmpeg cifs-utils flatpak \
|
||||
steam-installer steam-devices gamemode mangohud lutris vulkan-tools \
|
||||
docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin \
|
||||
nvidia-container-toolkit \
|
||||
code claude-desktop
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
step "NVIDIA driver (open kernel modules — the only kind the RTX 5090 supports)"
|
||||
apt_install ubuntu-drivers-common
|
||||
installed_driver() {
|
||||
{ dpkg-query -W -f='${Status} ${Package}\n' 'nvidia-driver-*' 2>/dev/null || true; } \
|
||||
| awk '/^install ok installed / {print $4}' | sort -V | tail -1
|
||||
}
|
||||
if [[ $(installed_driver) != *-open ]]; then
|
||||
ubuntu-drivers devices 2>/dev/null | grep -E 'model|driver' || true
|
||||
# Picks Ubuntu's recommended branch plus its prebuilt, signed kernel modules (no DKMS).
|
||||
sudo ubuntu-drivers install
|
||||
fi
|
||||
driver=$(installed_driver)
|
||||
[[ $driver == nvidia-driver-*-open ]] \
|
||||
|| die "got '${driver:-no driver}', but the 5090 needs an -open driver — check 'ubuntu-drivers devices'"
|
||||
branch=${driver#nvidia-driver-}; branch=${branch%-open}
|
||||
apt_install "libnvidia-gl-$branch:i386" # 32-bit GL/Vulkan for Steam and Proton
|
||||
echo "using $driver"
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
step "Docker, GPU containers, Claude's Cowork VM"
|
||||
sudo usermod -aG docker "$USER" # docker without sudo (after reboot)
|
||||
getent group kvm >/dev/null && sudo usermod -aG kvm "$USER" # Cowork needs /dev/kvm + vhost-vsock
|
||||
echo vhost_vsock | sudo tee /etc/modules-load.d/vhost_vsock.conf >/dev/null
|
||||
if ! grep -q nvidia /etc/docker/daemon.json 2>/dev/null; then
|
||||
sudo nvidia-ctk runtime configure --runtime=docker # enables `docker run --gpus all`
|
||||
sudo systemctl restart docker
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
step "Flatpak apps"
|
||||
sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
mapfile -t apps < <(sed -e 's/#.*//' -e 's/[[:space:]]//g' -e '/^$/d' "$REPO/flatpaks.txt")
|
||||
sudo flatpak install -y --noninteractive --system flathub "${apps[@]}"
|
||||
# Firefox's read-only peek at landingPage, as set by hand on Bazzite
|
||||
flatpak override --user org.mozilla.firefox --filesystem="$HOME/Documents/git/landingPage:ro"
|
||||
|
||||
# Firefox/Thunderbird come from Flathub instead, so restoring ~/.var/app brings the old
|
||||
# profiles back untouched. Drop Ubuntu's snaps and the debs that pull them back in.
|
||||
for s in firefox thunderbird; do
|
||||
if snap list "$s" >/dev/null 2>&1; then sudo snap remove --purge "$s"; fi
|
||||
if dpkg -s "$s" >/dev/null 2>&1; then sudo apt-get remove -y "$s"; fi
|
||||
done
|
||||
xdg-settings set default-web-browser org.mozilla.firefox.desktop 2>/dev/null || true
|
||||
|
||||
# Keep Flatpaks updated daily, like Bazzite did. Starts next boot.
|
||||
sudo tee /etc/systemd/system/flatpak-update.service >/dev/null <<'EOF'
|
||||
[Unit]
|
||||
Description=Update system Flatpaks
|
||||
Wants=network-online.target
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/flatpak update --system --noninteractive --assumeyes
|
||||
EOF
|
||||
sudo tee /etc/systemd/system/flatpak-update.timer >/dev/null <<'EOF'
|
||||
[Unit]
|
||||
Description=Update system Flatpaks daily
|
||||
|
||||
[Timer]
|
||||
OnBootSec=10min
|
||||
OnUnitActiveSec=1d
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
EOF
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable flatpak-update.timer
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
step "Claude Code CLI"
|
||||
# No Node or .NET on the host by choice — LudosData and landingPage build in Docker.
|
||||
# If you want them back: sudo apt install mise dotnet-sdk-10.0 (mise needs ppa:jdxcode/mise).
|
||||
command -v claude >/dev/null || [[ -x ~/.local/bin/claude ]] || curl -fsSL https://claude.ai/install.sh | bash
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
step "Data drive + server-marvin share"
|
||||
sudo mkdir -p "$DATA_MNT" "$MARVIN_MNT"
|
||||
if ! grep -q "$DATA_UUID" /etc/fstab; then
|
||||
echo "UUID=$DATA_UUID $DATA_MNT ext4 defaults,nofail,x-gvfs-show,x-gvfs-name=Data 0 2" \
|
||||
| sudo tee -a /etc/fstab >/dev/null
|
||||
fi
|
||||
sudo blkid -U "$DATA_UUID" >/dev/null || warn "Data drive ($DATA_UUID) not found — fstab entry is nofail, so boot is fine"
|
||||
|
||||
if [[ ! -f $MARVIN_CREDS ]]; then
|
||||
read -rp "server-marvin SMB username: " smb_user
|
||||
read -rsp "server-marvin SMB password: " smb_pass; echo
|
||||
sudo install -d -m 0755 /etc/samba
|
||||
printf 'username=%s\npassword=%s\n' "$smb_user" "$smb_pass" \
|
||||
| sudo install -m 600 /dev/stdin "$MARVIN_CREDS"
|
||||
unset smb_pass
|
||||
fi
|
||||
if ! grep -q "$MARVIN_MNT" /etc/fstab; then
|
||||
echo "$MARVIN_SHARE $MARVIN_MNT cifs credentials=$MARVIN_CREDS,uid=$(id -u),gid=$(id -g),iocharset=utf8,vers=3.0,x-systemd.automount,_netdev,nofail 0 0" \
|
||||
| sudo tee -a /etc/fstab >/dev/null
|
||||
fi
|
||||
sudo systemctl daemon-reload
|
||||
sudo mount -a || warn "a mount failed — check 'sudo mount -a' (is server-marvin up? right password?)"
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
step "Small carry-overs from Bazzite"
|
||||
# Xbox controllers over Bluetooth (Bazzite set this as a kernel argument)
|
||||
echo 'options bluetooth disable_ertm=1' | sudo tee /etc/modprobe.d/bluetooth-xbox.conf >/dev/null
|
||||
|
||||
step "Done"
|
||||
cat <<EOF
|
||||
Reboot now (NVIDIA driver + docker/kvm groups take effect), then:
|
||||
scripts/restore.sh
|
||||
EOF
|
||||
Reference in New Issue
Block a user