Some sites use logs/ instead of log/ (e.g. webscroll.fr/logs/nginx/) -
the previous re:/log/ pattern didn't match the plural, so those files
kept showing up as modified every backup.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Offsite sync hit 17 fatal IO errors deleting stale segment files after a
large one-off prune batch (from the recent log/-exclude change), and
rclone's default behavior is to bail immediately on delete errors rather
than retry. Lower concurrency (--transfers 8->4, --checkers 16->8) to
reduce rate-limit pressure, and make retries explicit (--retries 5,
--retries-sleep 10s, --low-level-retries 20) instead of relying on
defaults.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
nginx access/error logs (and any other per-site log/ dir) change on
every request, so they showed up as modified in every single backup -
pure noise, no recovery value. Exclude any path containing a log/
directory (re:/log/) from borg create.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dump_users_and_grants() called log(), which dump_db.sh never defines
(only die()/warn() exist) - every real run printed "log: command not
found" on stderr while still completing successfully. Use echo, matching
the rest of the script's style.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CLAUDE.md previously said the repo was encrypted, which was true when
written but no longer matches this deployment - the operator chose to
stay unencrypted, so the recurring "not encrypted" warning is expected
behavior, not something to fix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
dump_db.sh no longer lives under $TARGET, so its own default dump
directory (relative to wherever the script is) would land outside the
backed-up tree. Pin DUMP_SCRIPT to /opt/backup-agent/dump_db.sh and
export DUMP_DIR explicitly so dumps still land in $TARGET/mariadb/dump
regardless of where the script itself is deployed.
Also drop the healthcheck integration (HEALTHCHECK_URL, send_healthcheck,
curl requirement) from borg-backup.sh per request - no monitoring hook
wanted for now.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Final review of the restore.sh branch found cmd_full restored every
database via `docker exec` before starting the container, which fails
immediately in the exact scenario full restore exists for (a freshly
rebuilt, stopped container). Reorders to extract -> start container ->
restore DBs.
Also, while touching cmd_full:
- Extract directly into place (cd / && borg extract) instead of staging
a full copy under /tmp then cp -a'ing it into $TARGET - halves disk
usage and restore time.
- Replace `rm -rf "$TARGET"/*` with `find "$TARGET" -mindepth 1 -delete`
so dotfiles don't survive a --force wipe.
- Add acquire_lock() (shares borg-backup.sh's lockfile so a restore and
the nightly backup cron can't run concurrently) and preflight()
(passphrase file readable, repo reachable) before any real work in
full/db/file.
Test isolation: mock borg/docker/mysql/mariadb consistently via a
BASH_ENV shim (previously only db-mode's test worked around PATH
shadowing by a real docker binary; every mocked test needed it, and a
missing `flock` mock broke everything once acquire_lock was added,
since flock(1) doesn't exist on macOS). Tests also isolate LOCKFILE and
BORG_PASSPHRASE_FILE to throwaway paths.
RUNBOOK.md: fix the quarterly drill command (borg extract has no
--destination flag, and needs `borg list --short` for a bare archive
name), reword the full-restore --force comment which read backwards,
and document the MYSQL_ROOT_PASSWORD/RESTORE_LOGDIR env overrides and
where restore logs land.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds container_running/container_health/wait_for_container/start_db
helpers and cmd_full(), plus tests. Fixed a delimiter bug in the two
new tests' sed scripts (the literal `${TARGET#/}` replacement text
collided with `#` as the sed delimiter) and relaxed the dry-run
no-external-calls assertion to allow the legitimate borg-list call
from resolve_archive(), mirroring the existing db dry-run test.