feat(backup): add rsync --link-dest off-host transport, restore docs
CI / ci (pull_request) Failing after 5m37s
CI / ci (pull_request) Failing after 5m37s
backup.sh now supports BACKUP_REMOTE_RSYNC alongside BACKUP_REMOTE. The rsync path writes <root>/YYYY-MM-DD/<archive> on the target with --link-dest pointing at the previous day's directory, so unchanged archives become hard links and daily snapshots cost almost zero extra bytes. BACKUP_REMOTE_SSH_KEY routes the rsync ssh leg to a dedicated identity (e.g. rsync.net restricted accounts). Timer moved to 03:00 Europe/Berlin (was 03:17 UTC) per #41. docs/operations.md: full restore procedure (parallel stack first, then atomic swap) plus the rsync vs rclone trade-off. Closes most of #41 — the only remaining task is the operator's choice of off-host target.
This commit is contained in:
+28
-1
@@ -13,10 +13,18 @@
|
||||
# Optional env:
|
||||
# BACKUP_DIR where to write archives (default /var/backups/librenotes)
|
||||
# BACKUP_REMOTE rclone target for off-site copy (e.g. s3:bucket/path)
|
||||
# BACKUP_REMOTE_RSYNC rsync destination for hard-link-deduplicated
|
||||
# off-host copy, e.g. backup@rsync.net:librenotes/
|
||||
# Layout written at the target:
|
||||
# <root>/YYYY-MM-DD/librenotes-<ts>.tar.gz
|
||||
# Each run passes --link-dest=../<previous>/ so
|
||||
# unchanged archives cost zero extra bytes.
|
||||
# BACKUP_REMOTE_SSH_KEY path to a private SSH key used for the rsync
|
||||
# leg (passed via -e "ssh -i <key>").
|
||||
# BACKUP_VERSION version string written into info.txt
|
||||
#
|
||||
# The script is intentionally a single self-contained file so it
|
||||
# can run on a minimal host with only sqlite3, tar, gzip, and
|
||||
# can run on a minimal host with only sqlite3, tar, gzip, rsync, and
|
||||
# (optionally) rclone.
|
||||
|
||||
set -euo pipefail
|
||||
@@ -58,3 +66,22 @@ if [ -n "${BACKUP_REMOTE:-}" ]; then
|
||||
rclone copy "$archive" "$BACKUP_REMOTE" --quiet
|
||||
echo "uploaded to $BACKUP_REMOTE"
|
||||
fi
|
||||
|
||||
if [ -n "${BACKUP_REMOTE_RSYNC:-}" ]; then
|
||||
# Compute today / previous-day directory names. Layout at the
|
||||
# remote target is <root>/YYYY-MM-DD/. We pass --link-dest pointing
|
||||
# at yesterday's dir so unchanged archives become hard links —
|
||||
# ~free for daily snapshots that are mostly identical.
|
||||
today="$(date -u +%Y-%m-%d)"
|
||||
yesterday="$(date -u -d 'yesterday' +%Y-%m-%d 2>/dev/null \
|
||||
|| date -u -v-1d +%Y-%m-%d)"
|
||||
ssh_opts=()
|
||||
if [ -n "${BACKUP_REMOTE_SSH_KEY:-}" ]; then
|
||||
ssh_opts=(-e "ssh -i $BACKUP_REMOTE_SSH_KEY -o StrictHostKeyChecking=accept-new")
|
||||
fi
|
||||
# Strip any trailing slash so we control the join.
|
||||
remote="${BACKUP_REMOTE_RSYNC%/}"
|
||||
rsync -a --link-dest="../$yesterday/" "${ssh_opts[@]}" \
|
||||
"$archive" "$remote/$today/"
|
||||
echo "rsync'd $archive -> $remote/$today/"
|
||||
fi
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
Description=Run librenotes backup nightly
|
||||
|
||||
[Timer]
|
||||
# 03:17 UTC nightly with up to 5min jitter so multiple machines on
|
||||
# the same schedule don't all hit the off-site target at once.
|
||||
OnCalendar=*-*-* 03:17:00 UTC
|
||||
# 03:00 Europe/Berlin nightly with up to 5min jitter so multiple
|
||||
# machines on the same schedule don't all hit the off-site target
|
||||
# at once.
|
||||
OnCalendar=*-*-* 03:00:00 Europe/Berlin
|
||||
RandomizedDelaySec=5min
|
||||
Persistent=true
|
||||
Unit=librenotes-backup.service
|
||||
|
||||
Reference in New Issue
Block a user