1# Dawarich migration
2
3The 2026-10-04 offline handoff includes Dawarich's PostgreSQL 18 database, with a verified restore and hashes for every table, sequence, and large object. Restore it before starting Dawarich. Keep the original Rails `SECRET_KEY_BASE` and copy `/var/app/storage` into the managed service dataset before the first application boot.
4
5The production database import on 2026-10-04 passed all 63 exported relation and large-object fingerprints before any Dawarich process started. Evidence is `/var/lib/studio/dawarich-import-7hw6v9w1/verified.json`. PostGIS retains its 8,500 built-in spatial references; PostgreSQL extension dumps contain only custom reference rows. Removing the built-ins changes geometry JSON serialization as well as breaking reference lookup. The importer preserves built-ins and replaces only explicitly dumped custom SRIDs.
6
7[import-personal-postgres.py](import-personal-postgres.py) reads the allocated production database from `nomad/jobs/dawarich/inputs/database`. The target database and `svc_dawarich` role must already exist. For initial production migration, keep both Nomad jobs stopped and use an isolated PostGIS initializer with `--network none`, mounting the final PostgreSQL data directory:
8
9```sh
10python3 tools/import-personal-postgres.py dawarich \
11 /mnt/storage1/apps/studio-handoff/20261004T230507Z-e75419 \
12 --container PERSONAL_POSTGRES_INITIALIZER
13```
14
15The importer saves a private target backup under `/var/lib/studio/dawarich-import-*`, restores application objects as the allocated owner, preserves PostGIS reference data, and compares the result with the export's content hashes. It leaves Dawarich stopped. Stop and remove the initializer before deploying the production Postgres job. For a later import into an already running production Postgres allocation, omit `--container`; Dawarich must still be stopped.
16
17The historical preview results below concern the rehearsal VM. Zenith's separate Redis dump was 86,557 bytes and belonged to Dawarich. DB 0 contained caches and track-generation keys; DB 1 held 106 queued `Family::Invitations::CleanupJob` entries. The old image's nightly schedule sent them to `family`, while its worker listened to `families`; that queue reached 107 entries on 2026-09-27. The pinned image schedules this job on `families`, and all 14 of its scheduled queues appear in the worker's queue list.
18
19The rehearsal's source and target used PostgreSQL 18 with PostGIS. The tested transfer used a consistent custom-format dump made with `pg_dump -Fc --no-owner --no-acl --exclude-extension=postgis`, then restored it into a **separate stage database** after creating the PostGIS extension there. It did not write to Zenith or the VM's production database.
20
21The Rails `SECRET_KEY_BASE` must stay the same as the source value so encrypted records remain readable. The old `/var/app/storage` contained one file; its checksum matched the staged copy. The generated `/var/app/public` assets and `/var/app/tmp` cache were not imported. The stage used its own Keycloak client and database; its two imported user records remained intact.
22
23Before the new image ran, the source and staged database both had 22,666 points, 1,011 tracks, 2 users, and 74 visits. The current image rebuilds derived tracks and visits on startup; after a worker restart, the staged database had 22,666 points, 698 tracks, 2 users, 139 visits, and 74 newly generated track segments. Track count is therefore not a row-for-row import check. Point and user counts, storage checksums, HTTPS health, and the SSO login flow should be checked separately.
24
25The first staged boot exposed a startup race: Sidekiq loaded the old `TrackSegment` schema before the web entrypoint finished migrations, then logged `unknown attribute 'start_at'`. Restarting the worker after migrations restored segment creation. The service now runs Redis as a prestart sidecar and completes a migration task before starting web and worker. Its web entrypoint still repeats the upstream migration command, which is idempotent but extends cold startup on the emulated x86 VM.
26
27The corrected deployment is `dawarich-preview-1e4dac4a` at `https://dawarich-preview-1e4dac4a.studio.test` (release `554d23a23d940690`). Nomad marked it healthy; the HTTPS health endpoint returned 200 with certificate verification, and the new worker allocation logged no `start_at` errors.
28
29[import-dawarich.sh](import-dawarich.sh) repeats the preview restore from the latest read-only Zenith app snapshot and copies the small storage directory. It compares the source and target `SECRET_KEY_BASE` by hash, backs up the target ZFS dataset and database, restores the source PostgreSQL dump with PostGIS, and checks point/user counts before starting the preview. The 2026-09-26 run completed successfully: 22,666 points and two users survived migrations, HTTPS health returned 200 from this Mac with certificate verification, Nomad marked the job healthy, and the worker had no `start_at` error.
30
31The shell importer requires the old running source and serves VM previews. The Python importer uses the retained offline handoff for the same-machine production cutover.