Files
diskless-workstation/serving/fleet-manager/backup.sh
T
Audiobuzz 08847532ee fleet-manager: pull non-CI images through the Harbor mirror
Route the ad-hoc images I introduced through harbor.bitasylum.org (the standing
rule): backup.sh uses harbor.bitasylum.org/dockerhub/library/alpine:3.20 (via an
overridable ALPINE_IMAGE); the DEPLOY.md volume-chown fix + the goacme/lego prose
use the Harbor paths too. Compose (influxdb/traefik) + Dockerfile bases are already
harbor-mirrored; git.bitasylum.org/audiobuzz/* stays (CI-built).
2026-07-05 01:28:06 +00:00

60 lines
2.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# backup.sh — snapshot the fleet-manager durable state to timestamped tarballs.
#
# Two docker named volumes hold everything that isn't reproducible from the image:
# - fleet-data : inventory.json + admins.json + enroll.key (CRITICAL — losing
# admins.json/enroll.key locks every admin out of the UI)
# - influx-data : the metrics time-series (history; regenerates going forward)
#
# Runs a throwaway alpine container to tar each volume read-only into $DEST. Safe
# to run live. Schedule it (systemd timer / cron); copy $DEST offsite.
#
# Usage:
# ./backup.sh [dest-dir] # default dest: ./backups
# COMPOSE_PROJECT_NAME=foo ./backup.sh # if you run compose under another project
#
# Restore (manager stopped):
# docker run --rm -v <project>_fleet-data:/dest -v "$PWD/backups":/src \
# harbor.bitasylum.org/dockerhub/library/alpine:3.20 \
# sh -c 'rm -rf /dest/* && tar xzf /src/fleet-data-YYYYmmdd-HHMMSS.tar.gz -C /dest'
set -euo pipefail
DEST=${1:-./backups}
PROJECT=${COMPOSE_PROJECT_NAME:-fleet-manager} # compose derives volume names as <project>_<key>
STAMP=$(date -u +%Y%m%d-%H%M%S)
VOLUMES=${BACKUP_VOLUMES:-"fleet-data influx-data"}
# Non-CI images pull through the Harbor mirror; overridable for other registries.
ALPINE_IMAGE=${ALPINE_IMAGE:-harbor.bitasylum.org/dockerhub/library/alpine:3.20}
mkdir -p "$DEST"
DEST_ABS=$(cd "$DEST" && pwd)
for key in $VOLUMES; do
vol="${PROJECT}_${key}"
if ! docker volume inspect "$vol" >/dev/null 2>&1; then
echo "backup: SKIP $vol (no such volume — wrong COMPOSE_PROJECT_NAME?)"
continue
fi
out="${key}-${STAMP}.tar.gz"
echo "backup: $vol -> $DEST_ABS/$out"
docker run --rm \
-v "${vol}:/src:ro" \
-v "${DEST_ABS}:/backup" \
"$ALPINE_IMAGE" tar czf "/backup/${out}" -C /src .
done
# Retention: keep the newest $KEEP of each volume's tarballs (0 = keep all).
KEEP=${BACKUP_KEEP:-14}
if [ "$KEEP" -gt 0 ]; then
for key in $VOLUMES; do
# ls -t for mtime order; our filenames are safe (key + UTC timestamp).
# shellcheck disable=SC2012
ls -1t "$DEST_ABS/${key}-"*.tar.gz 2>/dev/null | tail -n +"$((KEEP + 1))" | while read -r old; do
echo "backup: prune $old"
rm -f "$old"
done
done
fi
echo "backup: done ($STAMP) — copy $DEST_ABS offsite."