Add database backup script with daily cron and 7-day retention

pg_dump-based backup script deployed alongside compose file. Deploy
script now installs a daily cron job (03:00) on Unraid automatically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Julian Tabel
2026-02-10 11:55:27 +01:00
parent 7b383dd982
commit 58475d9cba
4 changed files with 54 additions and 5 deletions

View File

@@ -1,10 +1,11 @@
--- ---
# nuzlocke-tracker-48ds # nuzlocke-tracker-48ds
title: Database backup strategy title: Database backup strategy
status: todo status: completed
type: task type: task
priority: normal
created_at: 2026-02-09T15:30:55Z created_at: 2026-02-09T15:30:55Z
updated_at: 2026-02-09T15:30:55Z updated_at: 2026-02-10T10:55:15Z
parent: nuzlocke-tracker-ahza parent: nuzlocke-tracker-ahza
--- ---

View File

@@ -54,5 +54,5 @@ Define and implement a deployment strategy for running the nuzlocke-tracker in p
- [x] **Configure Nginx Proxy Manager** — add proxy host entries for Gitea and the nuzlocke-tracker frontend/API on the appropriate ports - [x] **Configure Nginx Proxy Manager** — add proxy host entries for Gitea and the nuzlocke-tracker frontend/API on the appropriate ports
- [x] **Environment & secrets management** — deploy script auto-generates `.env` with `POSTGRES_PASSWORD` on Unraid if missing; file lives at `/mnt/user/appdata/nuzlocke-tracker/.env` - [x] **Environment & secrets management** — deploy script auto-generates `.env` with `POSTGRES_PASSWORD` on Unraid if missing; file lives at `/mnt/user/appdata/nuzlocke-tracker/.env`
- [ ] **Implement Gitea Actions CI/CD pipeline** — set up Gitea Actions runner on Unraid, create CI workflow (lint/test on `develop`) and deploy workflow (build/push/deploy on `main`); uses GitHub Actions-compatible syntax for portability - [ ] **Implement Gitea Actions CI/CD pipeline** — set up Gitea Actions runner on Unraid, create CI workflow (lint/test on `develop`) and deploy workflow (build/push/deploy on `main`); uses GitHub Actions-compatible syntax for portability
- [ ] **Database backup strategy** — set up a simple scheduled backup for the PostgreSQL data (e.g., cron + `pg_dump` script on Unraid) - [x] **Database backup strategy** — set up a simple scheduled backup for the PostgreSQL data (e.g., cron + `pg_dump` script on Unraid)
- [ ] **Document the deployment workflow** — README or docs covering how to deploy, redeploy, rollback, and manage the production instance - [ ] **Document the deployment workflow** — README or docs covering how to deploy, redeploy, rollback, and manage the production instance

33
backup.sh Executable file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
# ── Configuration ──────────────────────────────────────────────
DEPLOY_DIR="/mnt/user/appdata/nuzlocke-tracker"
BACKUP_DIR="${DEPLOY_DIR}/backups"
RETENTION_DAYS=7
DB_SERVICE="db"
DB_NAME="nuzlocke"
DB_USER="postgres"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/nuzlocke-${TIMESTAMP}.sql.gz"
# ── Create backup directory ───────────────────────────────────
mkdir -p "$BACKUP_DIR"
# ── Dump database ─────────────────────────────────────────────
cd "$DEPLOY_DIR"
docker compose exec -T "$DB_SERVICE" pg_dump -U "$DB_USER" "$DB_NAME" | gzip > "$BACKUP_FILE"
echo "Backup created: ${BACKUP_FILE}"
# ── Rotate old backups ────────────────────────────────────────
find "$BACKUP_DIR" -name "nuzlocke-*.sql.gz" -mtime +${RETENTION_DAYS} -delete
REMAINING=$(find "$BACKUP_DIR" -name "nuzlocke-*.sql.gz" | wc -l)
echo "Backups retained: ${REMAINING}"
# ── Restore procedure ────────────────────────────────────────
# To restore from a backup:
# cd /mnt/user/appdata/nuzlocke-tracker
# gunzip -c backups/nuzlocke-YYYYMMDD-HHMMSS.sql.gz | \
# docker compose exec -T db psql -U postgres nuzlocke

View File

@@ -55,10 +55,13 @@ done
info "All images built and pushed." info "All images built and pushed."
# ── Sync compose file to Unraid ────────────────────────────────── # ── Sync compose file to Unraid ──────────────────────────────────
info "Copying docker-compose.prod.yml to Unraid..." info "Copying docker-compose.prod.yml and backup.sh to Unraid..."
scp docker-compose.prod.yml "${UNRAID_SSH}:${UNRAID_DEPLOY_DIR}/docker-compose.yml" \ scp docker-compose.prod.yml "${UNRAID_SSH}:${UNRAID_DEPLOY_DIR}/docker-compose.yml" \
|| error "Failed to copy compose file to Unraid." || error "Failed to copy compose file to Unraid."
info "Compose file synced." scp backup.sh "${UNRAID_SSH}:${UNRAID_DEPLOY_DIR}/backup.sh" \
|| error "Failed to copy backup script to Unraid."
ssh "${UNRAID_SSH}" "chmod +x '${UNRAID_DEPLOY_DIR}/backup.sh'"
info "Compose file and backup script synced."
# ── Ensure .env with Postgres password exists ──────────────────── # ── Ensure .env with Postgres password exists ────────────────────
info "Checking for .env on Unraid..." info "Checking for .env on Unraid..."
@@ -72,6 +75,18 @@ ssh "${UNRAID_SSH}" "
fi fi
" || error "Failed to check/create .env on Unraid." " || error "Failed to check/create .env on Unraid."
# ── Ensure daily backup cron job exists ───────────────────────────
info "Checking for backup cron job on Unraid..."
CRON_CMD="0 3 * * * ${UNRAID_DEPLOY_DIR}/backup.sh >> ${UNRAID_DEPLOY_DIR}/backups/cron.log 2>&1"
ssh "${UNRAID_SSH}" "
if ! crontab -l 2>/dev/null | grep -qF '${UNRAID_DEPLOY_DIR}/backup.sh'; then
(crontab -l 2>/dev/null; echo '${CRON_CMD}') | crontab -
echo 'Cron job installed (daily at 03:00)'
else
echo 'Cron job already exists, skipping'
fi
" || error "Failed to set up backup cron job on Unraid."
# ── Pull images and (re)start on Unraid ────────────────────────── # ── Pull images and (re)start on Unraid ──────────────────────────
info "Pulling images and starting containers on Unraid..." info "Pulling images and starting containers on Unraid..."
ssh "${UNRAID_SSH}" "cd '${UNRAID_DEPLOY_DIR}' && docker compose pull && docker compose up -d" \ ssh "${UNRAID_SSH}" "cd '${UNRAID_DEPLOY_DIR}' && docker compose pull && docker compose up -d" \