#!/usr/bin/env bash # Nightly backup of Forgejo state to a second sia.storage bucket via restic. # Belt-and-suspenders: sia.storage's ToS caps liability at ~$100 and doesn't # guarantee data retention on account termination. This is our recoverability. # # Contents: # - /data/git (bare repos on VPS local disk) # - postgres (pg_dump of the forgejo database) # # Restic itself lives in a small sidecar container; this script is intended to # be scheduled via `cron` on the VPS host, or `docker compose exec` from a # systemd timer. See docker-compose.override.yml.example (todo) for wiring. # # Retention: keep-daily=7 keep-weekly=4 keep-monthly=12 set -euo pipefail # shellcheck disable=SC1091 [[ -f "$(dirname "$0")/../.env" ]] && source "$(dirname "$0")/../.env" : "${SIA_STORAGE_ENDPOINT:?}" : "${SIA_STORAGE_ACCESS_KEY:?}" : "${SIA_STORAGE_SECRET_KEY:?}" : "${SIA_STORAGE_BUCKET_BACKUP:=${SIA_STORAGE_BUCKET}-backup}" : "${RESTIC_PASSWORD:?RESTIC_PASSWORD must be set in .env (generate with: openssl rand -base64 32)}" export AWS_ACCESS_KEY_ID="$SIA_STORAGE_ACCESS_KEY" export AWS_SECRET_ACCESS_KEY="$SIA_STORAGE_SECRET_KEY" export RESTIC_REPOSITORY="s3:https://${SIA_STORAGE_ENDPOINT}/${SIA_STORAGE_BUCKET_BACKUP}" export RESTIC_PASSWORD # Init on first run (idempotent — swallows "already initialised") restic snapshots >/dev/null 2>&1 || restic init # 1. Postgres dump (pipe into restic's stdin backup mode) docker compose exec -T postgres pg_dump -U "${POSTGRES_USER}" "${POSTGRES_DB}" \ | restic backup --stdin --stdin-filename postgres-forgejo.sql --tag postgres # 2. Git repositories (mounted volume path inside the forgejo container) docker compose exec forgejo tar -cf - /data/git \ | restic backup --stdin --stdin-filename forgejo-git.tar --tag git # 3. Prune old snapshots restic forget --prune \ --keep-daily 7 \ --keep-weekly 4 \ --keep-monthly 12 # 4. Verify a sample of the repo (cheap integrity check) restic check --read-data-subset=5% echo "backup ok at $(date -Iseconds)"