lotus-land-story: drop the mountain-of-faith restic mirror
lolk is now the sole repository; mof keeps its existing snapshots, but new ones stop flowing there. Docs updated for the single-repo setup and the phase tracing.

Assisted-by: GLM-5.3 via pi
change uyzqtstkquulvwstqzxkwsvwlvtmmyvl
commit 0c345a05cd5a7c169c367d861f9b73d4a044f963
author Alpha Chen <alpha@kejadlen.dev>
date
parent uyvxtopv
diff --git a/docs/backup.md b/docs/backup.md
index 6da45d4..c64dfd3 100644
--- a/docs/backup.md
+++ b/docs/backup.md
@@ -1,9 +1,8 @@
 # Backups
 
-A daily cron script backs lotus-land-story up with restic into a primary
-repository on mountain-of-faith, then mirrors every snapshot into a secondary
-repository on legacy-of-lunatic-kingdom. Losing either machine loses at most
-one day of backups.
+A daily cron script backs lotus-land-story up with restic into a repository
+on legacy-of-lunatic-kingdom. That repository is the only copy: losing lolk
+loses the backups along with it.
 
 ## The daily run
 
@@ -12,62 +11,48 @@ one day of backups.
 `lotus-land-story/backup.yml`) runs as root. cron has no MTA, so the script
 sends its own output to the journal with the `backup` tag. It sets
 `GOGC=20` so restic trades CPU for memory, since lotus-land-story has little
-headroom and the OOM killer has taken restic mid-backup. Each run:
+headroom and the OOM killer has taken restic mid-backup.
+
+Each phase — unlock, pro-tacts, files, postgres, forget — logs a `> name`
+breadcrumb when it starts and `< name: Ns` when it finishes, so the journal
+shows where a slow or dead run sits. Each run:
 
 1. Pings healthchecks.io `/start`, distinguishing a run that died early from
    one that never fired.
-2. Removes stale locks from the primary, so a run killed mid-flight (for
-   example, by the OOM killer) doesn't block every later prune. The
-   secondary gets the same treatment just before the copy, so a down lolk
-   can't stop the primary backup.
-3. Backs up `/mnt/lotus-land-story/`, excluding `docker/`.
-4. Backs up `pg_dumpall` output via stdin as `lotus-land-story.sql`.
-5. Runs `forget --prune` against the primary with retention of daily for one
-   month, weekly for one year, monthly forever.
-6. Mirrors every snapshot onto the secondary with
-   `restic copy --from-repo <primary>`.
+2. Dumps pro-tacts' database with `docker exec pro-tacts rake db:dump`,
+   which writes under `/data` ahead of the file backup.
+3. Removes stale locks from the repository, so a run killed mid-flight (for
+   example, by the OOM killer) doesn't block every later prune.
+4. Backs up `/mnt/lotus-land-story/`, excluding `docker/` and the live
+   `postgresql*` cluster — a file-level copy of a running cluster can't be
+   restored, so the dump in step 5 is the backup. The pro-tacts dump from
+   step 2 rides along.
+5. Backs up `pg_dumpall` output via stdin as `lotus-land-story.sql`.
+6. Runs `forget --prune` with retention of daily for one month, weekly for
+   one year, monthly forever.
 
 An exit trap pings healthchecks.io with success or failure and pushes
-`backup_*` metrics to VictoriaMetrics. Telemetry failures never change the
-script's own exit status, but the copy in step 6 does count toward it: a
-broken mirror fails the run.
-
-## Repositories
-
-- Primary: `sftp:restic@mountain-of-faith:/mnt/mountain-of-faith/restic`
-- Secondary: `sftp:restic@legacy-of-lunatic-kingdom:/mnt/data/restic`
+`backup_*` metrics — including per-phase `backup_phase_duration_seconds` —
+to VictoriaMetrics. Telemetry failures never change the script's own exit
+status.
 
-Both live under a `restic` system user on the host, reachable by SSH keys for
-lotus-land-story (the backup) and 1password (manual restores). The
-repositories share one password, stored in `host_vars/lotus-land-story.sops.yml`.
+## Repository
 
-Restic resolves the source repository's password separately from
-`RESTIC_PASSWORD`, so the script exports `RESTIC_FROM_PASSWORD` for the copy.
-The repository URLs are defined once as play vars in `lotus-land-story/backup.yml`
-and templated into the script.
+`sftp:restic@legacy-of-lunatic-kingdom:/mnt/data/restic`, defined as a play
+var in `lotus-land-story/backup.yml` and templated into the script. It lives
+under a `restic` system user, reachable by SSH keys for lotus-land-story
+(the backup) and 1password (manual restores). The password is stored in
+`host_vars/lotus-land-story.sops.yml`.
 
 ## What the playbooks handle
 
 - The backup play trusts lolk's host key by reading it over Ansible and
   writing it to known_hosts; the script's sftp connection fails closed on an
   unknown host key.
-- The play probes the secondary with `restic cat config` and, if missing,
-  initializes it with `init --from-repo <primary> --copy-chunker-params`.
-  Copy requires an existing destination, and matching chunker parameters let
-  the mirror deduplicate against the primary instead of storing a second copy
-  of every pack file.
 - lolk's playbook (`legacy-of-lunatic-kingdom/main.yml`) hosts the repository
   (user, keys, directory), so it has to be applied before the backup play can
   first reach `restic@lolk`.
 
-## The secondary is append-only
-
-`forget --prune` runs against the primary only, so the secondary accumulates
-every snapshot since the mirror started. If the NAS ever becomes the primary,
-drop the copy step from `lotus-land-story/templates/backup.sh`, point
-`RESTIC_REPOSITORY` at the NAS repository, and run a one-time
-`forget --prune` there with the same retention flags.
-
 ## Verifying a run
 
 ```sh
@@ -75,5 +60,5 @@ ssh lotus-land-story sudo /etc/cron.daily/backup
 ssh lotus-land-story journalctl -t backup
 ```
 
-The journal shows the restic output including the copy; healthchecks.io
-should show the success ping for the same run.
+The journal shows the restic output and the phase breadcrumbs;
+healthchecks.io should show the success ping for the same run.
diff --git a/lotus-land-story/backup.yml b/lotus-land-story/backup.yml
index d8a2338..d7f0460 100644
--- a/lotus-land-story/backup.yml
+++ b/lotus-land-story/backup.yml
@@ -2,7 +2,6 @@
   hosts: lotus-land-story
   vars:
     primary_repository: sftp:restic@legacy-of-lunatic-kingdom:/mnt/data/restic
-    secondary_repository: sftp:restic@mountain-of-faith:/mnt/mountain-of-faith/restic
   tasks:
 
     - name: Install Restic
@@ -31,26 +30,6 @@
         name: legacy-of-lunatic-kingdom
         key: "legacy-of-lunatic-kingdom {{ lolk_host_key.content | b64decode }}"
 
-    # restic copy fails on a missing destination, and --copy-chunker-params
-    # keeps dedup working across the two repositories.
-    - name: Check for secondary Restic repository
-      ansible.builtin.command:
-        cmd: restic --repo {{ secondary_repository }} cat config
-      environment:
-        RESTIC_PASSWORD: "{{ restic.password }}"
-      register: secondary_repo
-      changed_when: false
-      failed_when: false
-
-    - name: Initialize secondary Restic repository
-      ansible.builtin.command: >-
-        restic --repo {{ secondary_repository }}
-        init --from-repo {{ primary_repository }} --copy-chunker-params
-      environment:
-        RESTIC_PASSWORD: "{{ restic.password }}"
-        RESTIC_FROM_PASSWORD: "{{ restic.password }}"
-      when: secondary_repo.rc != 0
-
     - name: Set up daily backup
       ansible.builtin.template:
         src: templates/backup.sh
diff --git a/lotus-land-story/templates/backup.sh b/lotus-land-story/templates/backup.sh
index 2efec89..9e89c70 100644
--- a/lotus-land-story/templates/backup.sh
+++ b/lotus-land-story/templates/backup.sh
@@ -3,7 +3,6 @@
 set -euo pipefail
 
 readonly PRIMARY_REPOSITORY={{ primary_repository }}
-readonly SECONDARY_REPOSITORY={{ secondary_repository }}
 
 export RESTIC_REPOSITORY=${PRIMARY_REPOSITORY}
 export RESTIC_PASSWORD={{ restic.password }}
@@ -118,11 +117,3 @@ phase forget restic --verbose forget \
   --keep-within-weekly 1y \
   --keep-within-monthly 100y \
   --prune
-
-# Mirror onto mountain-of-faith while both are dual-write targets. Restic
-# resolves the source repository's password separately from RESTIC_PASSWORD, so
-# pass it explicitly; the repos share one.
-export RESTIC_REPOSITORY=${SECONDARY_REPOSITORY}
-export RESTIC_FROM_PASSWORD=${RESTIC_PASSWORD}
-phase unlock-secondary restic unlock
-phase mirror restic --verbose copy --from-repo "${PRIMARY_REPOSITORY}"