back up Gitea + Skudak app data; drop PartKeepr and Pi-hole

Extends the Nextcloud backup machinery rather than adding a second
mechanism. cloud-backup.sh.j2 gains three guarded options, all no-ops for
the existing callers:

  backup_podman_user  Gitea runs rootless under `git`, not `podman`
  backup_db_type      postgres (Gitea) and mysql (BookStack) alongside
                      mariadb; each engine's completion trailer differs,
                      and grepping for the wrong one fails every run
  backup_sqlite_dbs   `sqlite3 .backup` for live WAL-mode SQLite, gated on
                      `pragma integrity_check` before promotion -- rsync
                      is either stale (no -wal) or torn (with it)

New instances: gitea-debyl, skudak-gitea, bookstack, partsy-skudak. The
alert handler is rendered once and shared, so its wording is now generic
rather than per-product; TAG stays nextcloud-backup because an external
Graylog rule matches on it.

`apply:` on the includes is load-bearing -- tags on a dynamic
include_tasks do not reach the tasks inside it.

Business data (skudak-gitea, bookstack, partsy-skudak) goes to TrueNAS
and on to Skudak's own iDrive account; the personal bucket's
/skudak*/** excludes are permanent, not a stopgap.

Removals: PartKeepr is superseded by Partsy, and its teardown never
finished -- it targeted /etc/systemd/system/podman-partkeepr*.service,
wrong prefix and wrong scope, leaving enabled user units in failed state.
Pi-hole's role was already orphaned (absent from deploy_home.yml); its
port 53 rule went with it after confirming nothing listens there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bastian de Byl
2026-07-30 18:08:47 -04:00
parent a63bf5edec
commit bc110ce69e
15 changed files with 236 additions and 129 deletions
@@ -161,6 +161,32 @@
cron_container: skudak-cloud
cron_script_path: /usr/local/bin/skudak-cloud-cron.sh
# This instance is BUSINESS data and deliberately backs up to TrueNAS ONLY.
#
# It used to reach personal cloud storage too: the TrueNAS "iDrive E2 Backup"
# cloud-sync task pushes /mnt/glacier to a personal iDrive e2 bucket, which
# swept skudakcloud/ along with it. That task now carries an explicit
# `/skudakcloud/**` exclude, and on 2026-07-30 the stranded copy was purged
# from the bucket -- business data does not belong in personal storage.
#
# The copy was also worthless as a backup: 30 objects against 20,802 files on
# TrueNAS (0.14%), stale since 2026-05-20. Worse, the bucket is VERSIONED and
# the sync runs in COPY mode (never deletes), so every daily run retained
# another ~60 GB version of the pre-cap nextcloud.log -- 56 of them, 3.46 TB,
# 99.3% of a 3.49 TB footprint. Deleting current objects alone reclaims
# nothing on a versioned bucket; the versions must be purged explicitly.
#
# Offsite is now BUSINESS-OWNED: Skudak's own iDrive e2 account, bucket
# `backup-all`, pushed by TrueNAS cloud-sync task "Skudak iDrive - Nextcloud"
# (id 8, /mnt/glacier/skudakcloud -> /skudakcloud, daily 06:00). That bucket
# has a 90-day NoncurrentVersionExpiration policy so the version bloat above
# cannot repeat. The personal task's `/skudakcloud/**` exclude is PERMANENT --
# it is what keeps business data out of personal storage, not a stopgap.
#
# Still outstanding: the data itself lives on personal TrueNAS hardware. To
# finish separating, add an S3 stage to cloud-backup.sh.j2 guarded by a
# `backup_s3_*` var so only this instance opts in -- awscli2 is already
# installed on the host -- and then drop the TrueNAS rsync below.
- include_tasks: containers/cloud-backup.yml
vars:
backup_name: skudak-cloud