backup script

This commit is contained in:
Stefan Ostermann 2025-07-23 14:04:24 +00:00
parent 3105c81a35
commit 16bf248308
1 changed files with 107 additions and 0 deletions

107
backup.sh Executable file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -Eeuo pipefail
shopt -s inherit_errexit nullglob
# -----------------------------------------------------------------------------
# CONFIGURATION — adjust to taste
# -----------------------------------------------------------------------------
readonly RETENTION_DAYS=7
readonly BACKUP_BASE=/media/extension/backup/ubuntu
readonly MOUNT_POINT=/media/extension
# timestamp format — no spaces
#readonly NOW=$(date +'%Y-%m-%d_%H-%M-%S')
readonly NOW=$(date +'%Y-%m-%d')
readonly THIS_RUN=${BACKUP_BASE}/${NOW}
readonly LATEST=${BACKUP_BASE}/latest
# directories (or files) to back up:
declare -a SOURCE_ITEMS=(
"/home/oster/server"
"/home/oster/photo-lib"
)
# rsync exclusions — add your own
declare -a EXCLUDES=(
--exclude='.cache'
--exclude='node_modules'
--exclude='*.tmp'
)
# rsync niceties
RSYNC_OPTS=(
-aAXHv # archive, preserve ACLs/xattrs, human-readable, verbose
--delete # mirror deletions
--partial # keep partials if interrupted
--stats # give stats at end
"${EXCLUDES[@]}"
)
# Dry-run? set to "--dry-run" to test without writing
DRY_RUN=""
# -----------------------------------------------------------------------------
# LOGGING & LOCKING / CLEANUP
# -----------------------------------------------------------------------------
log() { echo "[$(date +'%F %T')] $*"; }
die() { log "FATAL: $*"; exit 1; }
lockfile="${BACKUP_BASE}/.backup.lock"
#trap 'rm -rf "$lockfile"' EXIT
#if ! touch "$lockfile" 2>/dev/null; then
# die "Another backup is running (lockfile exists)."
#fi
# -----------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------
check_mount() {
if ! mountpoint -q "$MOUNT_POINT"; then
die "Backup mountpoint $MOUNT_POINT is not mounted."
fi
}
rotate_old() {
log "Rotating backups older than ${RETENTION_DAYS} days."
find "$BACKUP_BASE" -maxdepth 1 -mindepth 1 -type d \
! -name 'latest' ! -name "$(basename "$THIS_RUN")" \
-mtime +${RETENTION_DAYS} \
-print -exec rm -rf {} \;
}
# -----------------------------------------------------------------------------
# MAIN
# -----------------------------------------------------------------------------
log "=== Starting backup run: $NOW ==="
check_mount
mkdir -p "$BACKUP_BASE"
mkdir -p "$THIS_RUN"
# Determine if we can do a hardlink-incremental snapshot
if [ -d "$LATEST" ]; then
LINK_DEST="--link-dest=$(readlink -f $LATEST)"
log "Using linkdest for incremental snapshot → $LATEST"
else
LINK_DEST=""
log "No previous backup; doing full snapshot."
fi
for src in "${SOURCE_ITEMS[@]}"; do
base=$(basename "$src")
dst="${THIS_RUN}/${base}"
log "Backing up $src$dst"
nice -n 10 ionice -c2 -n7 \
rsync "${RSYNC_OPTS[@]}" $LINK_DEST $DRY_RUN \
"$src"/ "$dst"/
done
# update the "latest" symlink atomically
tmp_latest="${BACKUP_BASE}/.latest.tmp"
rm -f "$tmp_latest"
ln -sfn "$(basename "$THIS_RUN")" "$tmp_latest"
mv -T "$tmp_latest" "$LATEST"
rotate_old
log "=== Backup completed successfully ==="