45 lines
2.1 KiB
Bash
45 lines
2.1 KiB
Bash
#!/usr/bin/env bash
|
|
# Nightly backup: MySQL dump + uploads/exports -> S3 (versioned bucket).
|
|
#
|
|
# Install on the server (as the user that runs docker):
|
|
# crontab -e
|
|
# 30 2 * * * /opt/ar-aging/ar-aging-app/deploy/backup.sh >> /var/log/ar-backup.log 2>&1
|
|
#
|
|
# Requires: aws cli v2 on the host, an instance IAM role with s3:PutObject/ListBucket on
|
|
# the bucket (no access keys on disk), and .env.production next to docker-compose.prod.yml.
|
|
set -euo pipefail
|
|
|
|
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$APP_DIR"
|
|
|
|
# shellcheck disable=SC1091
|
|
set -a; source .env.production; set +a
|
|
BUCKET="${AR_BACKUP_S3_BUCKET:?AR_BACKUP_S3_BUCKET not set in .env.production}"
|
|
STAMP="$(date +%Y-%m-%d_%H%M)"
|
|
COMPOSE="docker compose --env-file .env.production -f docker-compose.prod.yml"
|
|
|
|
echo "[$STAMP] backup starting"
|
|
|
|
# 1) MySQL dump (single transaction: consistent snapshot without locking the app out).
|
|
$COMPOSE exec -T mysql sh -c \
|
|
'exec mysqldump --single-transaction --quick --routines \
|
|
-u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' \
|
|
| gzip > "/tmp/ar_aging_${STAMP}.sql.gz"
|
|
aws s3 cp "/tmp/ar_aging_${STAMP}.sql.gz" "$BUCKET/mysql/ar_aging_${STAMP}.sql.gz"
|
|
rm -f "/tmp/ar_aging_${STAMP}.sql.gz"
|
|
|
|
# 2) Uploaded source files + generated exports (the audit trail).
|
|
# The ar_data volume is mounted by the backend container; sync straight from it.
|
|
DATA_MOUNT="$(docker volume inspect -f '{{ .Mountpoint }}' \
|
|
"$(basename "$APP_DIR" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9')_ar_data" 2>/dev/null \
|
|
|| docker volume inspect -f '{{ .Mountpoint }}' ar-aging-app_ar_data)"
|
|
aws s3 sync "$DATA_MOUNT/uploads" "$BUCKET/data/uploads" --only-show-errors
|
|
aws s3 sync "$DATA_MOUNT/exports" "$BUCKET/data/exports" --only-show-errors
|
|
|
|
echo "[$STAMP] backup finished"
|
|
|
|
# Restore drill (run quarterly — a backup you never restored is a hope, not a backup):
|
|
# aws s3 cp "$BUCKET/mysql/<latest>.sql.gz" - | gunzip | \
|
|
# docker compose --env-file .env.production -f docker-compose.prod.yml exec -T mysql \
|
|
# sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"'
|