updates
This commit is contained in:
108
backup.sh
108
backup.sh
@@ -3,70 +3,90 @@ set -Eeuo pipefail
|
||||
shopt -s inherit_errexit nullglob
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# CONFIGURATION — adjust to taste
|
||||
# CONFIGURATION
|
||||
# -----------------------------------------------------------------------------
|
||||
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
|
||||
readonly THIS_RUN="${BACKUP_BASE}/${NOW}"
|
||||
readonly LATEST="${BACKUP_BASE}/latest"
|
||||
|
||||
# directories (or files) to back up:
|
||||
# Directories (or files) to back up (without DB volumes)
|
||||
declare -a SOURCE_ITEMS=(
|
||||
"/home/oster/server"
|
||||
"/home/oster/photo-lib"
|
||||
# "/home/oster/photo-lib"
|
||||
)
|
||||
|
||||
# rsync exclusions — add your own
|
||||
# Rsync exclusions — exclude DB-related dirs that are dumped separately + common junk
|
||||
declare -a EXCLUDES=(
|
||||
--exclude='.cache'
|
||||
--exclude='node_modules'
|
||||
--exclude='*.tmp'
|
||||
--exclude='**/postgresql'
|
||||
--exclude='**/mysql'
|
||||
--exclude='**/db'
|
||||
--exclude='**/database'
|
||||
)
|
||||
|
||||
# rsync niceties
|
||||
# Rsync options
|
||||
RSYNC_OPTS=(
|
||||
-aAXHv # archive, preserve ACLs/xattrs, human-readable, verbose
|
||||
-aAXHv # preserve everything, human-friendly, verbose
|
||||
--delete # mirror deletions
|
||||
--partial # keep partials if interrupted
|
||||
--stats # give stats at end
|
||||
--stats # summary
|
||||
"${EXCLUDES[@]}"
|
||||
)
|
||||
|
||||
# Dry-run? set to "--dry-run" to test without writing
|
||||
DRY_RUN=""
|
||||
# -----------------------------------------------------------------------------
|
||||
# LOGGING
|
||||
# -----------------------------------------------------------------------------
|
||||
log() { echo "[$(date +'%F %T')] $*"; }
|
||||
die() { log "FATAL: $*"; exit 1; }
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# LOGGING & LOCKING / CLEANUP
|
||||
# -----------------------------------------------------------------------------
|
||||
log() { echo "[$(date +'%F %T')] $*"; }
|
||||
die() { log "FATAL: $*"; exit 1; }
|
||||
# Locking
|
||||
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
|
||||
cleanup() { rm -f "$lockfile"; }
|
||||
trap cleanup EXIT
|
||||
if ! ( set -o noclobber; echo "$$" > "$lockfile" ) 2>/dev/null; then
|
||||
die "Another backup is already running (lockfile exists at $lockfile)."
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# FUNCTIONS
|
||||
# -----------------------------------------------------------------------------
|
||||
check_mount() {
|
||||
if ! mountpoint -q "$MOUNT_POINT"; then
|
||||
die "Backup mountpoint $MOUNT_POINT is not mounted."
|
||||
die "Backup mountpoint $MOUNT_POINT is NOT mounted."
|
||||
fi
|
||||
}
|
||||
|
||||
# Rotation: keep 7 daily and 4 weekly
|
||||
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 {} \;
|
||||
log "Applying retention policy (7 daily, 4 weekly)."
|
||||
cd "$BACKUP_BASE" || die "Cannot cd into $BACKUP_BASE"
|
||||
|
||||
# find snapshot dirs YYYY-MM-DD only
|
||||
for dir in $(ls -1 | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'); do
|
||||
keep=0
|
||||
|
||||
# Keep last 7 daily
|
||||
for i in $(seq 0 6); do
|
||||
[ "$dir" = "$(date -d "-$i day" +%F)" ] && keep=1
|
||||
done
|
||||
|
||||
# Keep last 4 Sundays (weeklies)
|
||||
for i in $(seq 0 27); do
|
||||
d=$(date -d "last sunday -$i week" +%F)
|
||||
[ "$dir" = "$d" ] && keep=1
|
||||
done
|
||||
|
||||
if [ $keep -eq 0 ]; then
|
||||
log " Deleting expired snapshot $dir"
|
||||
rm -rf "$dir"
|
||||
else
|
||||
log " Keeping snapshot $dir"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -75,33 +95,33 @@ rotate_old() {
|
||||
log "=== Starting backup run: $NOW ==="
|
||||
check_mount
|
||||
|
||||
mkdir -p "$BACKUP_BASE"
|
||||
mkdir -p "$THIS_RUN"
|
||||
mkdir -p "$BACKUP_BASE" "$THIS_RUN"
|
||||
|
||||
# Determine if we can do a hardlink-incremental snapshot
|
||||
# Incremental snapshot?
|
||||
if [ -d "$LATEST" ]; then
|
||||
LINK_DEST="--link-dest=$(readlink -f $LATEST)"
|
||||
log "Using link‐dest for incremental snapshot → $LATEST"
|
||||
LINK_DEST="--link-dest=$(readlink -f "$LATEST")"
|
||||
log "Using link-dest for incremental snapshot → $LATEST"
|
||||
else
|
||||
LINK_DEST=""
|
||||
log "No previous backup; doing full snapshot."
|
||||
log "No previous backup; creating full snapshot."
|
||||
fi
|
||||
|
||||
# Run backups
|
||||
for src in "${SOURCE_ITEMS[@]}"; do
|
||||
base=$(basename "$src")
|
||||
dst="${THIS_RUN}/${base}"
|
||||
mkdir -p "$dst"
|
||||
log "Backing up $src → $dst"
|
||||
|
||||
nice -n 10 ionice -c2 -n7 \
|
||||
rsync "${RSYNC_OPTS[@]}" $LINK_DEST $DRY_RUN \
|
||||
rsync "${RSYNC_OPTS[@]}" $LINK_DEST \
|
||||
"$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"
|
||||
# Update latest symlink atomically
|
||||
ln -sfn "$(basename "$THIS_RUN")" "$BACKUP_BASE/.latest.tmp"
|
||||
mv -T "$BACKUP_BASE/.latest.tmp" "$LATEST"
|
||||
|
||||
log "Snapshots done. Running rotation policy..."
|
||||
rotate_old
|
||||
|
||||
log "=== Backup completed successfully ==="
|
||||
|
||||
Reference in New Issue
Block a user