1# Same-machine PostgreSQL handoff
2
3The retained apps dataset is an encrypted ZFS root. Keep its mountpoint explicitly at `/mnt/storage1/apps` after renaming the pool to `globe` and replacing Zenith with NixOS; [media-cutover.md](media-cutover.md) gives the command order. The old app directories and `/mnt/storage1/apps/home-infra/.env` remain there; the new `/srv/prod` root is separate.
4
5If TrueNAS is still running, stop the old apps being migrated, leaving old PostgreSQL running long enough to export. Run `bash tools/export-legacy-postgres.sh` before replacing the OS. Zenith grants the `clo` account passwordless `sudo docker` access for the export; this online exporter requires the original `storage1/apps` mount and exports Forgejo and HedgeDoc only. It does not stop apps itself.
6
7If already booted into the installer, [export-offline-postgres.py](export-offline-postgres.py) exports every connectable database and all roles from both retained clusters. It requires all PostgreSQL servers to be stopped, matching-major runtimes with the source extensions installed, and `glibcLocales`. It starts private RAM copies over private Unix sockets, saves complete physical archives including `template0`, checks the original file hashes remain unchanged, and restores every logical dump into fresh temporary clusters. Verification compares every user table's contents, sequence state, and large objects. The originals are never started. Run as root on the installer, with the runtime paths supplied by the pinned Nix builds:
8
9```sh
10python3 tools/export-offline-postgres.py \
11 --cluster /mnt/storage1/apps/pg_data "$postgres17_runtime" \
12 --cluster /mnt/storage1/apps/pg_data18/18/docker "$postgres18_runtime" \
13 --locale-archive "$glibc_locales/lib/locale/locale-archive"
14```
15
16It publishes a private `studio-handoff/<timestamp>-<suffix>` directory only after successful restores. Its `pg17` and `pg18` directories contain all database dumps, `globals.sql`, and `cluster.tar.gz`; the manifest records their hashes and restore evidence. Root-level Forgejo and HedgeDoc dump links select the PG18 exports for the existing importers. Dawarich's old database is retained in the full export even though its new service starts fresh. Copy the complete handoff directory, preserving these links, to a private off-server archive and verify the file hashes before replacing the OS. `globals.sql` contains role password hashes; protect the archive like the recovery keys. Failed exports remain under `.pending-*` and cannot be selected by the importers.
17
18The October 4 installer export restored and verified nine databases and eleven roles from each cluster. Its complete archive is saved on the Mac at `/Users/clo/Library/Application Support/Zenith Recovery/postgres-backups/20261004T230507Z-e75419.tar`; every archived file hash and both physical cluster contents were checked against the server manifest. The production handoff is:
19
20```sh
21export STUDIO_LEGACY_HANDOFF=/mnt/storage1/apps/studio-handoff/20261004T230507Z-e75419
22```
23
24After NixOS has imported and unlocked the apps dataset, set `STUDIO_DEPLOY_HOST`, `STUDIO_DEPLOY_PORT`, and `STUDIO_LEGACY_HANDOFF` to that printed directory. The importers require the mounted pool's encrypted apps dataset at `/mnt/storage1/apps` and a stopped destination job. HedgeDoc and evil.inc Forgejo verify dump hashes and source table counts before restoring their PostgreSQL databases. Shale, Jellyfin, Navidrome, PDS, qBittorrent, Sonarr, Radarr, Jackett, and YouTube Triage can copy their retained app data without the old Docker daemon. Each importer prints its pre-import backup or ZFS snapshot.
25
26`import-legacy-secrets.sh` reads the retained old `.env` from the new host when `STUDIO_LEGACY_HANDOFF` is set. It sends only the requested keys into Snow Globe's Nomad variables; it does not print their values. The handoff directory must exist on the target. Without that variable, the script continues reading from the old Zenith host for a two-host migration.
27
28The handoff contains no Forgejo repository files. [import-evil-forgejo.sh](import-evil-forgejo.sh) checks its `evil-forgejo.dump`, secret fingerprints, and the stopped Snow Globe job, then copies the old 14 GB app directory directly from the mounted dataset. The Forgejo job stays stopped until its files, database, and account identities have been checked together.