first commit
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# restic-backup.sh — generic backup script
|
||||
# Deployed to: /usr/local/bin/restic-backup.sh
|
||||
#
|
||||
# All machine-specific config lives in /etc/restic/env and
|
||||
# /etc/restic/excludes.txt — this script never needs editing.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
|
||||
|
||||
# Load environment — set -a auto-exports all variables to child processes
|
||||
set -a && source /etc/restic/env && set +a
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Server reachability check with retry
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
BACKUP_HOST="${BACKUP_SERVER%%:*}"
|
||||
BACKUP_PORT="${BACKUP_SERVER##*:}"
|
||||
MAX_RETRIES=3
|
||||
RETRY_MIN=600 # 10 min
|
||||
RETRY_MAX=1800 # 30 min
|
||||
|
||||
server_reachable() {
|
||||
# Dependency-free TCP check using bash built-ins
|
||||
(echo >/dev/tcp/"${BACKUP_HOST}"/"${BACKUP_PORT}") 2>/dev/null
|
||||
}
|
||||
|
||||
wait_for_server() {
|
||||
local attempt=1
|
||||
while ! server_reachable; do
|
||||
if [ "$attempt" -ge "$MAX_RETRIES" ]; then
|
||||
log "ERROR: Backup server ${BACKUP_SERVER} unreachable after ${MAX_RETRIES} attempts. Giving up."
|
||||
exit 1
|
||||
fi
|
||||
local delay=$(( RANDOM % (RETRY_MAX - RETRY_MIN + 1) + RETRY_MIN ))
|
||||
log "Backup server unreachable (attempt ${attempt}/${MAX_RETRIES}). Retrying in $(( delay / 60 )) min..."
|
||||
sleep "$delay"
|
||||
(( attempt++ ))
|
||||
done
|
||||
log "Backup server reachable."
|
||||
}
|
||||
|
||||
log "=== Backup started on $(hostname) (${MACHINE_NAME}) ==="
|
||||
wait_for_server
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Pre-backup hooks — dump system state so it's included in the snapshot
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Debian / Ubuntu
|
||||
if command -v dpkg &>/dev/null; then
|
||||
log "Dumping installed packages (dpkg)..."
|
||||
dpkg --get-selections > /etc/backup-package-list.txt
|
||||
# RHEL / Fedora / CentOS
|
||||
elif command -v rpm &>/dev/null; then
|
||||
log "Dumping installed packages (rpm)..."
|
||||
rpm -qa > /etc/backup-package-list.txt
|
||||
# Arch Linux
|
||||
elif command -v pacman &>/dev/null; then
|
||||
log "Dumping installed packages (pacman)..."
|
||||
pacman -Qqe > /etc/backup-package-list.txt
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Backup
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Running restic backup..."
|
||||
|
||||
# Word-split BACKUP_PATHS intentionally — each path is a separate argument
|
||||
# shellcheck disable=SC2086
|
||||
restic backup \
|
||||
$BACKUP_PATHS \
|
||||
--exclude-file /etc/restic/excludes.txt \
|
||||
--one-file-system \
|
||||
--verbose
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Retention
|
||||
# ---------------------------------------------------------------------------
|
||||
log "Applying retention policy (daily=${KEEP_DAILY} weekly=${KEEP_WEEKLY} monthly=${KEEP_MONTHLY})..."
|
||||
|
||||
restic forget \
|
||||
--keep-daily "${KEEP_DAILY}" \
|
||||
--keep-weekly "${KEEP_WEEKLY}" \
|
||||
--keep-monthly "${KEEP_MONTHLY}" \
|
||||
--prune
|
||||
|
||||
log "=== Backup finished on $(hostname) (${MACHINE_NAME}) ==="
|
||||
Reference in New Issue
Block a user