backlog-md/backlog/tasks/task-013 - Automated-databa...

1.9 KiB

id title status assignee created_date labels dependencies priority
task-013 Automated database backups and offsite storage Done
2026-03-15 09:00
dev-ops
enhancement
high

Description

No critical database has automated backups. No data leaves this single server. A disk failure or compromise would lose all source code (Gitea), business data (ERPNext), photos (Immich), wiki/blog content (p2p-db), and email (Mailcow). This is the highest priority infrastructure gap.

Acceptance Criteria

  • #1 Automated daily pg_dump for Gitea-DB (Postgres) — 3.1MB
  • #2 Automated daily mysqldump for ERPNext MariaDB — 2.3MB
  • #3 Automated daily mysqldump for p2p-db (MariaDB) — 707MB
  • #4 Automated daily pg_dump for Immich Postgres — 620MB
  • #5 Automated daily Mailcow backup (mariadb-dump) — 1.1MB
  • #6 Off-server storage — rclone sync to Cloudflare R2 (plex-media/db-backups/)
  • #7 Backup retention policy (7 days local, synced to R2)
  • #8 Backup monitoring — Uptime Kuma push monitor (ID 199, 48h heartbeat interval)

Notes

Approach: Centralized backup container

Deploy an Alpine container with cron, database clients (pg_dump, mariadb-dump), and rclone. Runs scheduled dumps for all databases, compresses, and pushes to R2.

Template

rphotos backup script at /opt/apps/rphotos-online/backup-database.sh has a good pattern (pg_dumpall with 30-day retention).

Database credentials needed

  • Gitea-DB: Postgres, accessible via gitea-db container
  • ERPNext: MariaDB at /opt/erpnext/ (host-only)
  • p2p-db: MariaDB, root password in container env
  • Immich: Postgres at /opt/immich/postgres (host bind mount)
  • Mailcow: MySQL in mailcowdockerized-mysql-mailcow-1

R2 storage

r2-mount container already has rclone configured for Cloudflare R2. Can reuse config for backup uploads.