backup: retire anca-elements-mirror + anca-elements-sync.sh
Some checks failed
ci/woodpecker/push/build-cli Pipeline failed
ci/woodpecker/push/default Pipeline was successful

Both subsumed by nfs-mirror (deployed earlier this session) — see
commit 4d756be4. anca-elements-sync.sh is now dead code because its
upstream (Synology /volume1/Backup/Anca/Elements) was deleted today
once the sda mirror was parity-verified (109,624 files /
827,480,937,976 bytes equal both sides). PVE NFS is the source of
truth for the archive from here on.

Final script inventory on the PVE host (down from 6 to 4):
- /usr/local/bin/daily-backup           (block PVCs + sqlite + pfsense)
- /usr/local/bin/lvm-pvc-snapshot       (snapshot management)
- /usr/local/bin/nfs-mirror             (NFS local mirror to sda)
- /usr/local/bin/offsite-sync-backup    (sda + bypass-list NFS to Synology)
This commit is contained in:
Viktor Barzin 2026-05-24 14:58:30 +00:00
parent 9e2163040b
commit 15745eab2f
4 changed files with 0 additions and 219 deletions

View file

@ -1,15 +0,0 @@
[Unit]
Description=Mirror /srv/nfs/anca-elements to /mnt/backup (single-disk-failure protection)
After=network-online.target local-fs.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/anca-elements-mirror
StandardOutput=journal
StandardError=journal
SyslogIdentifier=anca-elements-mirror
# Big sustained IO — don't compete with foreground services.
Nice=10
IOSchedulingClass=idle
TimeoutStartSec=18000

View file

@ -1,82 +0,0 @@
#!/usr/bin/env bash
# anca-elements-mirror — single-disk-failure mirror of /srv/nfs/anca-elements → /mnt/backup
#
# Deploy to PVE host at /usr/local/bin/anca-elements-mirror.
# Schedule: weekly Mon 04:00 via systemd timer (anca-elements-mirror.timer).
#
# WHY: /srv/nfs/anca-elements lives on the sdc thin pool. Synology no longer
# holds the original (deleted after this mirror was verified). sda /mnt/backup
# is the only other local disk with room (~770G) — this gives us a single-
# disk-failure copy. No offsite for this archive (intentional, see backup-dr.md).
#
# Idempotent: `rsync -aH --delete` makes destination match source exactly.
# Re-runs only transfer changed files.
set -euo pipefail
SRC=/srv/nfs/anca-elements
DST=/mnt/backup/anca-elements
LOG=/var/log/anca-elements-mirror.log
LOCKFILE=/run/anca-elements-mirror.lock
PUSHGATEWAY="${ANCA_MIRROR_PUSHGATEWAY:-http://10.0.20.100:30091}"
PUSHGATEWAY_JOB=anca-elements-mirror
log() { echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*" | tee -a "$LOG"; }
warn() { log "WARN: $*"; }
push_metrics() {
local status="${1:-0}" bytes="${2:-0}"
cat <<EOF | curl -s --connect-timeout 5 --max-time 10 --data-binary @- "${PUSHGATEWAY}/metrics/job/${PUSHGATEWAY_JOB}" 2>/dev/null || true
anca_elements_mirror_last_run_timestamp $(date +%s)
anca_elements_mirror_last_status ${status}
anca_elements_mirror_bytes ${bytes}
EOF
}
KILLED=""
cleanup() {
rm -f "$LOCKFILE"
if [ -n "$KILLED" ]; then
push_metrics 2 0 # status=2 → aborted (matches lvm-pvc-snapshot convention)
fi
}
trap cleanup EXIT
trap 'KILLED=1; exit 143' TERM INT
if ! ( set -o noclobber; echo $$ > "$LOCKFILE" ) 2>/dev/null; then
log "FATAL: another instance running (pid $(cat "$LOCKFILE" 2>/dev/null || echo unknown))"
exit 1
fi
mountpoint -q /mnt/backup || { log "FATAL: /mnt/backup not mounted"; push_metrics 1 0; exit 1; }
[ -d "$SRC" ] || { log "FATAL: source $SRC missing"; push_metrics 1 0; exit 1; }
mkdir -p "$DST"
log "=== mirror starting: $SRC$DST ==="
SRC_SIZE_GB=$(du -sBG "$SRC" 2>/dev/null | awk '{print $1}')
log "source size: $SRC_SIZE_GB"
# -aH preserves hardlinks (probably none here, cheap insurance).
# --info=stats2 emits a final transfer summary into the log.
# --no-perms / --no-owner / --no-group: source has root:www-data 2775 and
# we don't need to perfectly preserve those on the mirror copy — dest will
# inherit /mnt/backup's defaults. (Symmetric with anca-elements-sync.sh's
# choice when copying FROM Synology.)
RSYNC_RC=0
rsync \
-rlt --delete -H \
--no-perms --no-owner --no-group \
--info=stats2 \
"$SRC/" "$DST/" 2>&1 | tee -a "$LOG" || RSYNC_RC=${PIPESTATUS[0]}
DST_BYTES=$(du -sb "$DST" 2>/dev/null | awk '{print $1}')
if [ "$RSYNC_RC" -eq 0 ]; then
log "=== mirror complete; dest size: $(du -sh "$DST" | cut -f1) ==="
push_metrics 0 "$DST_BYTES"
else
log "=== mirror failed: rsync exited $RSYNC_RC ==="
push_metrics 1 "$DST_BYTES"
exit "$RSYNC_RC"
fi

View file

@ -1,10 +0,0 @@
[Unit]
Description=Weekly anca-elements mirror to /mnt/backup
[Timer]
OnCalendar=Mon *-*-* 04:00:00
Persistent=true
RandomizedDelaySec=15min
[Install]
WantedBy=timers.target

View file

@ -1,112 +0,0 @@
#!/usr/bin/env bash
# anca-elements-sync.sh — copy Anca's WD-Elements backup from Synology to PVE NFS
#
# Usage:
# /usr/local/bin/anca-elements-sync.sh
#
# Idempotent: re-running after a successful sync is a no-op (only the dry-run
# verification runs, which reports "sync verified clean" immediately).
#
# Resumable: if fpsync was interrupted, resume with:
# fpsync -r /var/tmp/fpsync \
# -n 4 -s 4G \
# -o "-lptgoD -H --no-perms --no-owner --no-group --exclude=@eaDir/ --exclude=*@synoeastream --exclude=.DS_Store --exclude=Thumbs.db" \
# /mnt/synology-backup/Anca/Elements/ /srv/nfs/anca-elements/
#
# NOTE: fpsync -o = rsync options override (what we want)
# fpsync -O = fpart partition options override (NOT rsync)
# NOTE: Do NOT use -a or -r in fpsync rsync options — fpsync handles
# recursion via fpart; -r causes fpsync to warn and skip the slab.
#
# Log: /var/log/anca-elements-sync.log
set -euo pipefail
LOG=/var/log/anca-elements-sync.log
SRC_HOST=192.168.1.13
SRC_EXPORT=/volume1/Backup
SRC_SUBPATH=Anca/Elements
MOUNT_POINT=/mnt/synology-backup
DEST=/srv/nfs/anca-elements
log() {
echo "[$(date -u '+%Y-%m-%dT%H:%M:%SZ')] $*" | tee -a "$LOG"
}
# ── 1. Ensure destination + mount-point directories exist ────────────────────
log "Step 1: ensuring directories"
mkdir -p "$DEST" "$MOUNT_POINT"
# ── 2. NFS-mount Synology read-only (skip if already mounted) ───────────────
MOUNTED_HERE=0
if mountpoint -q "$MOUNT_POINT"; then
log "Step 2: $MOUNT_POINT already mounted — skipping"
else
log "Step 2: mounting ${SRC_HOST}:${SRC_EXPORT} at $MOUNT_POINT (read-only)"
mount -t nfs \
-o ro,vers=4,nolock,soft,timeo=300,retrans=2 \
"${SRC_HOST}:${SRC_EXPORT}" \
"$MOUNT_POINT"
MOUNTED_HERE=1
log "Step 2: mount successful"
fi
# ── 3. Ensure fpsync (from fpart package) is available ──────────────────────
log "Step 3: checking for fpsync"
if ! command -v fpsync >/dev/null 2>&1; then
log "Step 3: fpsync not found — installing fpart"
apt-get install -y fpart
log "Step 3: fpart installed"
else
log "Step 3: fpsync already available"
fi
# ── 4. Run fpsync (4-way parallel, no compression — source is already-compressed media) ──
log "Step 4: starting fpsync"
log " source : ${MOUNT_POINT}/${SRC_SUBPATH}/"
log " dest : ${DEST}/"
log " workers: 4, slab: 4G"
fpsync \
-n 4 \
-s 4G \
-o "-lptgoD -H --no-perms --no-owner --no-group --exclude=@eaDir/ --exclude=*@synoeastream --exclude=.DS_Store --exclude=Thumbs.db" \
"${MOUNT_POINT}/${SRC_SUBPATH}/" \
"${DEST}/" \
2>&1 | tee -a "$LOG"
log "Step 4: fpsync completed"
# ── 5. Verification dry-run ──────────────────────────────────────────────────
log "Step 5: running dry-run verification rsync"
VERIFY_OUT=$(rsync \
-rlptgoD -H --no-perms --no-owner --no-group \
--exclude='@eaDir/' --exclude='*@synoeastream' \
--exclude='.DS_Store' --exclude='Thumbs.db' \
-n --delete \
--info=progress2 \
--out-format='%o %f' \
"${MOUNT_POINT}/${SRC_SUBPATH}/" \
"${DEST}/" \
2>&1 || true)
# Count lines that represent actual file changes (send / del. operations)
CHANGE_COUNT=$(echo "$VERIFY_OUT" | grep -cE '^(send|del\.)' || true)
if [ "$CHANGE_COUNT" -eq 0 ]; then
log "Step 5: sync verified clean — no pending changes"
else
log "Step 5: WARNING — verification found ${CHANGE_COUNT} pending change(s). First 50 lines:"
# Use printf to avoid SIGPIPE from head closing the pipe early (set -o pipefail)
{ echo "$VERIFY_OUT" | head -50; } >> "$LOG" 2>&1 || true
fi
# ── 6. Unmount (only if we mounted it) ──────────────────────────────────────
if [ "$MOUNTED_HERE" -eq 1 ]; then
log "Step 6: unmounting $MOUNT_POINT"
umount "$MOUNT_POINT"
rmdir "$MOUNT_POINT"
log "Step 6: unmounted"
else
log "Step 6: mount was pre-existing — leaving in place"
fi
log "Done. Final size: $(du -sh "${DEST}" | cut -f1)"