# Migration — onyx-qbt → nullstone-qbt State at time of writing: 60+ active torrents on onyx with download dirs on onyx local disk. **Goal:** keep seeding (don't burn ratios) while shifting future downloads to nullstone. Two-phase, no big-bang. --- ## Phase 1 — Stand up nullstone stack (no migration yet) 1. **Prep directory tree** on nullstone: ```bash ssh user@nullstone sudo mkdir -p /home/user/media/_downloads/{incomplete,complete,watch} sudo chown -R user:user /home/user/media/_downloads ``` 2. **Generate new Proton WG key + provisioning for gluetun.** Don't reuse `wg-pvpn-A` keys (they're host-routed; conflict risk). Log into Proton account → WireGuard → new key → name it `nullstone-gluetun-arr` → save the privkey + assigned address (e.g. `10.2.0.3/32`). 3. **Drop the privkey + address into `compose/.env`:** ```bash cd /opt/docker/media-acquisition/compose cp .env.example .env ${EDITOR:-vi} .env # Set: # PVPN_WG_PRIVKEY= # PVPN_WG_ADDRESSES=10.2.0.3/32 # PVPN_SERVER_COUNTRIES=Netherlands ``` 4. **Bring up the stack.** Start gluetun + qbt only first: ```bash docker compose up -d gluetun qbittorrent ``` 5. **Kill-switch test (NON-NEGOTIABLE):** ```bash bash scripts/killswitch-test.sh ``` If second curl succeeds → leak. Tear down and debug. Do not proceed. 6. **Sacrificial torrent.** Pick something legal + big you don't care about (e.g. a Linux distro ISO). Add it via qbt webui, watch it land in `/home/user/media/_downloads/complete/`. Confirm it **does not** appear in JF. 7. **Bring up the rest of the stack.** ```bash docker compose up -d ``` Configure Prowlarr → Sonarr → Radarr (in that order — Prowlarr pushes indexers downstream). Set "Use Hardlinks instead of Copy = yes" in Sonarr/Radarr Media Management. 8. **Test arr → import path.** Sonarr Interactive Search → manual grab → import into `/media/tv/...`. Verify catalog service commits to Forgejo. --- ## Phase 2 — Migrate onyx torrents For each active torrent on onyx that you want to keep seeding: ```bash # On onyx — export .torrent files + qbt's fastresume state mkdir -p /tmp/qbt-migrate cp ~/.local/share/qBittorrent/BT_backup/*.torrent /tmp/qbt-migrate/ cp ~/.local/share/qBittorrent/BT_backup/*.fastresume /tmp/qbt-migrate/ # rsync the actual data files to nullstone first (LAN gigabit) rsync -av --info=progress2 ~/Downloads/qbt/ \ user@192.168.0.100:/home/user/media/_downloads/complete/ ``` Then on nullstone qbt webui: 1. Add `.torrent` files in bulk via webui ("Add torrent files…"), save path = `/downloads/complete/`, **uncheck "Start torrent"**. 2. Force-recheck each added torrent. qbt matches local files → `100%` → seeding. 3. Verify trackers respond. Private trackers may need source-IP rotation — gluetun exit IP differs from onyx public IP. See `docs/trackers.md`. 4. On onyx: pause torrents one-by-one as nullstone takes over. Don't stop onyx-qbt entirely until every torrent shows seeding on nullstone for 24h with no tracker errors. **Catalog backfill:** for torrents that correspond to already-imported library items, **don't** trigger arr-import — they're already in canonical locations. Just seed from `_downloads/complete/`. Catalog stays accurate. For torrents that were mid-download on onyx but never made it into the library: re-add on nullstone, let them complete via VPN, then sonarr/radarr picks them up via the normal path. **Estimated migration window:** 1 weekend. ~250 GB rsync over LAN gigabit ≈ ~30 min wall clock for the data move, then a manual-but-tedious add-and-recheck loop in qbt. The wrapper script for steps 1-2 is at `scripts/migrate-onyx.sh`. It does the rsync + builds a `.torrent` index for a follow-up bulk-add. The fastresume-rewrite step is documented inline in the script. --- ## Phase 3 — Decommission onyx-qbt After 7 days clean on nullstone: 1. Stop qbt service on onyx (`systemctl --user stop qbittorrent-nox` or kill the GUI; depends on how it was launched). 2. Delete `~/Downloads/qbt/` on onyx (only after confirming no in-flight torrents reference it). 3. Update `ai-lab/CLAUDE.md` device registry note if onyx had a "downloads role" annotation. (As of 2026-05-20 it does not — onyx has been the staging host but is not formally documented as such.) 4. Optional: keep the `.torrent` files archive on onyx for 30 days as a safety net. --- ## Rollback If nullstone stack starts failing during phase 2: - `docker compose down` on nullstone. - Re-enable onyx qbt (Phase 1's stack is non-destructive — onyx torrents still have their data + fastresume). - File an issue + revisit phase 1 step 5 (kill-switch test).