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:
@@ -1,59 +0,0 @@
|
||||
---
|
||||
# PartKeepr has been replaced by Partsy
|
||||
# This playbook removes PartKeepr containers and services
|
||||
# Keeping MySQL data volume for historical reference only
|
||||
|
||||
- name: stop and remove partkeepr container
|
||||
become: true
|
||||
become_user: "{{ podman_user }}"
|
||||
containers.podman.podman_container:
|
||||
name: partkeepr
|
||||
state: absent
|
||||
|
||||
- name: stop and remove partkeepr-db container
|
||||
become: true
|
||||
become_user: "{{ podman_user }}"
|
||||
containers.podman.podman_container:
|
||||
name: partkeepr-db
|
||||
state: absent
|
||||
|
||||
- name: remove systemd service for partkeepr
|
||||
become: true
|
||||
ansible.builtin.systemd:
|
||||
name: "podman-partkeepr.service"
|
||||
state: stopped
|
||||
enabled: false
|
||||
daemon_reload: true
|
||||
ignore_errors: true
|
||||
|
||||
- name: remove systemd service for partkeepr-db
|
||||
become: true
|
||||
ansible.builtin.systemd:
|
||||
name: "podman-partkeepr-db.service"
|
||||
state: stopped
|
||||
enabled: false
|
||||
daemon_reload: true
|
||||
ignore_errors: true
|
||||
|
||||
- name: remove systemd service files for partkeepr
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "/etc/systemd/system/podman-partkeepr.service"
|
||||
- "/etc/systemd/system/podman-partkeepr-db.service"
|
||||
notify: systemd daemon-reload
|
||||
|
||||
- name: preserve partkeepr mysql data volume for history
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
path: "{{ partkeepr_path }}/mysql"
|
||||
state: directory
|
||||
owner: "{{ podman_subuid.stdout }}"
|
||||
group: "{{ podman_user }}"
|
||||
mode: 0755
|
||||
notify: restorecon podman
|
||||
|
||||
- name: flush handlers
|
||||
ansible.builtin.meta: flush_handlers
|
||||
@@ -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
|
||||
|
||||
@@ -15,12 +15,9 @@
|
||||
- 443/tcp
|
||||
# Gitea Skudak SSH
|
||||
- 2222/tcp
|
||||
# pihole (unused?)
|
||||
- 53/tcp
|
||||
- 53/udp
|
||||
# nosql/redis
|
||||
- 6379/tcp
|
||||
# ???
|
||||
# BookStack (wiki.skudak.com) -- container publishes 6875:8080
|
||||
- 6875/tcp
|
||||
# Satisfactory
|
||||
- 7777/tcp
|
||||
|
||||
@@ -34,12 +34,6 @@
|
||||
image: ghcr.io/home-assistant/home-assistant:2026.5.1
|
||||
tags: hass
|
||||
|
||||
- import_tasks: containers/home/partkeepr.yml
|
||||
vars:
|
||||
db_image: docker.io/library/mariadb:10.0
|
||||
image: docker.io/bdebyl/partkeepr:0.1.10
|
||||
tags: partkeepr
|
||||
|
||||
- import_tasks: containers/home/partsy.yml
|
||||
vars:
|
||||
image: "git.debyl.io/debyltech/partsy:latest"
|
||||
@@ -117,3 +111,126 @@
|
||||
image: docker.io/cm2network/steamcmd:root
|
||||
tags: zomboid
|
||||
|
||||
# ---------------------------------------------------------- Gitea backups
|
||||
# The Gitea pods themselves are owned by roles/git, but the backup machinery
|
||||
# (containers/cloud-backup.yml plus templates/nextcloud/*) lives here, and an
|
||||
# include_tasks reaching across roles would need a path outside the role. So
|
||||
# the two Gitea backup instances are wired here alongside the container ones.
|
||||
#
|
||||
# Both run as ROOTLESS podman under "{{ git_user }}", not "{{ podman_user }}"
|
||||
# -- hence backup_podman_user -- and use PostgreSQL rather than MariaDB.
|
||||
#
|
||||
# Scheduled ahead of the 04:00/04:30 Nextcloud runs so everything lands before
|
||||
# the 05:00 TrueNAS snapshot.
|
||||
# `apply` is required: tags on a dynamic include_tasks select whether the
|
||||
# include runs, but do NOT propagate to the tasks inside it, so without this
|
||||
# `make deploy TAGS=gitea-backup` includes the file and then filters out every
|
||||
# task in it. The Nextcloud instances avoid this only because they are reached
|
||||
# through a static import_tasks chain that tags their children at parse time.
|
||||
- include_tasks:
|
||||
file: containers/cloud-backup.yml
|
||||
apply:
|
||||
tags: gitea-backup
|
||||
vars:
|
||||
backup_name: gitea-debyl
|
||||
backup_product: Gitea
|
||||
backup_podman_user: "{{ git_user }}"
|
||||
data_path: "{{ git_home }}/volumes/gitea/data"
|
||||
db_container: gitea-debyl-postgres
|
||||
backup_db_type: postgres
|
||||
ssh_key_path: /etc/ssh/backup_keys/gitea
|
||||
ssh_key_content: "{{ gitea_backup_ssh_key }}"
|
||||
ssh_user: gitea
|
||||
remote_path: /mnt/glacier/gitea
|
||||
script_path: /usr/local/bin/gitea-backup.sh
|
||||
# actions_log/artifacts are CI churn (510 MB and growing) and rebuildable;
|
||||
# indexers/queues/tmp are derived state Gitea recreates on start. Note the
|
||||
# default excludes are Nextcloud-specific, so this must be set explicitly.
|
||||
backup_rsync_excludes: >-
|
||||
--exclude '/gitea/actions_log' --exclude '/gitea/actions_artifacts'
|
||||
--exclude '/gitea/tmp' --exclude '/gitea/indexers' --exclude '/gitea/queues'
|
||||
backup_oncalendar: "*-*-* 03:30:00"
|
||||
tags: gitea-backup
|
||||
|
||||
# ------------------------------------------------- Skudak app-data backups
|
||||
# BookStack (wiki.skudak.com) and partsy-skudak are BUSINESS data. Both share
|
||||
# one TrueNAS dataset (/mnt/glacier/skudakapps) so they need only one backup
|
||||
# user, key and cloud-sync task between them; the personal "iDrive E2 Backup"
|
||||
# task excludes /skudakapps/** and Skudak's own task pushes it to backup-all.
|
||||
#
|
||||
# Scheduled ahead of the 03:30+ Gitea/Nextcloud jobs and the 05:00 snapshot.
|
||||
- include_tasks:
|
||||
file: containers/cloud-backup.yml
|
||||
apply:
|
||||
tags: [skudak, skudak-apps-backup]
|
||||
vars:
|
||||
backup_name: bookstack
|
||||
backup_product: BookStack
|
||||
data_path: "{{ bookstack_path }}"
|
||||
db_container: bookstack-db
|
||||
# mysql:5.7 predates the mariadb-dump alias -- see cloud-backup.sh.j2.
|
||||
backup_db_type: mysql
|
||||
ssh_key_path: /etc/ssh/backup_keys/skudakapps
|
||||
ssh_key_content: "{{ skudakapps_backup_ssh_key }}"
|
||||
ssh_user: skudakapps
|
||||
remote_path: /mnt/glacier/skudakapps/bookstack
|
||||
script_path: /usr/local/bin/bookstack-backup.sh
|
||||
# The wiki content is the DATABASE; /mysql is its raw datadir, which must
|
||||
# not be rsynced live -- the dump above is the consistent copy. public/
|
||||
# and storage/ hold the uploads and are the only file trees worth shipping.
|
||||
backup_rsync_excludes: "--exclude '/mysql'"
|
||||
backup_oncalendar: "*-*-* 03:00:00"
|
||||
tags: skudak, skudak-apps-backup
|
||||
|
||||
- include_tasks:
|
||||
file: containers/cloud-backup.yml
|
||||
apply:
|
||||
tags: [skudak, skudak-apps-backup]
|
||||
vars:
|
||||
backup_name: partsy-skudak
|
||||
backup_product: Partsy
|
||||
data_path: "{{ partsy_skudak_path }}"
|
||||
# Live WAL-mode SQLite: snapshotted via `sqlite3 .backup` rather than
|
||||
# rsynced, so the -wal/-shm sidecars are deliberately excluded from the
|
||||
# file tree -- shipping them alongside a separately-taken snapshot would
|
||||
# only invite a confusing restore.
|
||||
backup_sqlite_dbs:
|
||||
- "{{ partsy_skudak_path }}/data/partsy.db"
|
||||
backup_rsync_excludes: "--exclude '*-wal' --exclude '*-shm'"
|
||||
ssh_key_path: /etc/ssh/backup_keys/skudakapps
|
||||
ssh_key_content: "{{ skudakapps_backup_ssh_key }}"
|
||||
ssh_user: skudakapps
|
||||
remote_path: /mnt/glacier/skudakapps/partsy-skudak
|
||||
script_path: /usr/local/bin/partsy-skudak-backup.sh
|
||||
backup_oncalendar: "*-*-* 03:10:00"
|
||||
tags: skudak, skudak-apps-backup
|
||||
|
||||
# BUSINESS data. Rsynced to TrueNAS here, then pushed offsite to SKUDAK'S OWN
|
||||
# iDrive e2 account (bucket `backup-all`) by TrueNAS cloud-sync task "Skudak
|
||||
# iDrive - Gitea" (id 9, /mnt/glacier/skudakgit -> /skudakgit, daily 06:30).
|
||||
#
|
||||
# The personal "iDrive E2 Backup" task's /skudakgit/** exclude is PERMANENT:
|
||||
# it is what keeps business data out of personal storage. Do not remove it --
|
||||
# Skudak has its own task and bucket instead.
|
||||
- include_tasks:
|
||||
file: containers/cloud-backup.yml
|
||||
apply:
|
||||
tags: [skudak, gitea-backup-skudak]
|
||||
vars:
|
||||
backup_name: skudak-gitea
|
||||
backup_product: Gitea
|
||||
backup_podman_user: "{{ git_user }}"
|
||||
data_path: "{{ git_home }}/volumes/gitea-skudak/data"
|
||||
db_container: gitea-skudak-postgres
|
||||
backup_db_type: postgres
|
||||
ssh_key_path: /etc/ssh/backup_keys/skudak-gitea
|
||||
ssh_key_content: "{{ skudakgit_backup_ssh_key }}"
|
||||
ssh_user: skudakgit
|
||||
remote_path: /mnt/glacier/skudakgit
|
||||
script_path: /usr/local/bin/skudak-gitea-backup.sh
|
||||
backup_rsync_excludes: >-
|
||||
--exclude '/gitea/actions_log' --exclude '/gitea/actions_artifacts'
|
||||
--exclude '/gitea/tmp' --exclude '/gitea/indexers' --exclude '/gitea/queues'
|
||||
backup_oncalendar: "*-*-* 03:45:00"
|
||||
tags: skudak, gitea-backup-skudak
|
||||
|
||||
|
||||
Reference in New Issue
Block a user