--env-file only feeds the containers; a plain shell script never saw BACKUP_DIR, so it silently fell back to ./backups while the Hub itself used the real value from .env. Found in production: postgres's /backups bind mount was still pinned to a directory orphaned by an earlier git-archive swap (mv gti-fiscal gti-fiscal_old && mv gti-fiscal_new gti-fiscal — Linux bind mounts follow the original dentry, not the path string), so every backup.sh run had been silently landing dumps nobody could find at the live path. Fixed by pointing BACKUP_DIR at a stable absolute path outside the deploy tree and recreating postgres once to pick it up; verified with a real ./backup.sh run in production. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
33 lines
2.1 KiB
Bash
33 lines
2.1 KiB
Bash
#!/usr/bin/env sh
|
|
# PostgreSQL + release-storage backup of the GTI Fiscal Hub (restorable with
|
|
# restore.sh + restore_releases.sh). Run from deploy/hub.
|
|
# ./backup.sh -> ./backups/gti_fiscal_YYYYmmdd_HHMMSS.dump (Postgres, last 14 kept)
|
|
# -> ./backups/gti_fiscal_releases_YYYYmmdd_HHMMSS.tgz (release binaries + AgentRelease metadata is IN the dump above)
|
|
set -eu
|
|
COMPOSE="docker compose -f docker-compose.prod.yml --env-file .env"
|
|
# BACKUP_DIR (like every other setting) lives in .env, but --env-file only
|
|
# feeds the container — a plain shell script never gets it unless it loads
|
|
# the file itself. Without this, BACKUP_DIR silently falls back to ./backups
|
|
# here while the Hub's containers still read the real value from .env.
|
|
if [ -f .env ]; then set -a; . ./.env; set +a; fi
|
|
DIR="${BACKUP_DIR:-./backups}"
|
|
mkdir -p "$DIR"
|
|
STAMP="$(date +%Y%m%d_%H%M%S)"
|
|
FILE="gti_fiscal_${STAMP}.dump"
|
|
$COMPOSE exec -T postgres pg_dump -U gti -d gti_fiscal -Fc -f "/backups/${FILE}"
|
|
[ -s "$DIR/$FILE" ] || { echo "backup empty or missing: $DIR/$FILE" >&2; exit 1; }
|
|
# Retention runs inside the container: Docker creates the bind-mounted
|
|
# backup directory as root, so a host-side rm would fail from the 15th dump.
|
|
$COMPOSE exec -T postgres sh -c 'ls -1t /backups/gti_fiscal_*.dump | tail -n +15 | xargs -r rm -f'
|
|
echo "backup ok: $DIR/$FILE"
|
|
|
|
# Release storage (central/app/release_storage.py: every published Agent
|
|
# package zip/exe, never in Postgres — only its metadata is, in the dump
|
|
# above). Streamed out of the fiscal-hub container's stdout so this works
|
|
# regardless of the compose project/container name.
|
|
RELEASES_FILE="gti_fiscal_releases_${STAMP}.tgz"
|
|
$COMPOSE exec -T fiscal-hub tar -C /app/data -cz releases > "$DIR/$RELEASES_FILE"
|
|
[ -s "$DIR/$RELEASES_FILE" ] || { echo "release backup empty or missing: $DIR/$RELEASES_FILE" >&2; exit 1; }
|
|
find "$DIR" -maxdepth 1 -name 'gti_fiscal_releases_*.tgz' -type f -printf '%T@ %p\n' 2>/dev/null | sort -rn | tail -n +15 | cut -d' ' -f2- | xargs -r rm -f
|
|
echo "release storage backup ok: $DIR/$RELEASES_FILE"
|