#!/bin/bash # Логический бэкап PostgreSQL (Nextcloud) из контейнера 101. # Запускать на хосте Proxmox под root. Использует pct exec (SSH не нужен). # Результат: /mnt/backup/databases/ct101-nextcloud/nextcloud-db-YYYYMMDD-HHMM.sql.gz set -e # Чтобы из cron находились bw и jq (часто в /usr/local/bin) export PATH="/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH:-}" CT_ID=101 BACKUP_DIR="/mnt/backup/databases/ct101-nextcloud" RETENTION_DAYS=14 # Имя контейнера БД в compose-проекте nextcloud (при смене имени в compose — поправить) PG_CONTAINER="nextcloud-db-1" if [ "$(id -u)" -ne 0 ]; then echo "Запускайте под root." exit 1 fi # Минимальный размер дампа (байт). Пустой gzip ≈ 20 байт — значит pg_dump не отдал данные. MIN_BACKUP_BYTES=512 mkdir -p "$BACKUP_DIR" DATE=$(date +%Y%m%d-%H%M) OUTPUT="$BACKUP_DIR/nextcloud-db-$DATE.sql.gz" ERR=$(mktemp) trap "rm -f '$ERR'" EXIT # PGPASSWORD из Vaultwarden (объект NEXTCLOUD: поле dbpassword или пароль). См. docs/backup/proxmox-phase1-backup.md PG_ENV_ARGS="" BW_MASTER_PASSWORD_FILE="${BW_MASTER_PASSWORD_FILE:-/root/.bw-master}" if [ -f "$BW_MASTER_PASSWORD_FILE" ] && command -v bw >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then export BW_SESSION=$(bw unlock --passwordfile "$BW_MASTER_PASSWORD_FILE" --raw 2>/dev/null) || true if [ -n "${BW_SESSION:-}" ]; then PGPASS=$(bw get item "NEXTCLOUD" 2>/dev/null | jq -r '.fields[] | select(.name=="dbpassword") | .value') [ -z "$PGPASS" ] && PGPASS=$(bw get password "NEXTCLOUD" 2>/dev/null) [ -n "$PGPASS" ] && PG_ENV_ARGS="-e PGPASSWORD=$PGPASS" fi fi pct exec $CT_ID -- docker exec $PG_ENV_ARGS "$PG_CONTAINER" pg_dump -U nextcloud nextcloud 2>"$ERR" | gzip > "$OUTPUT" SIZE_BYTES=$(stat -c%s "$OUTPUT" 2>/dev/null || echo 0) if [ -s "$OUTPUT" ] && [ "$SIZE_BYTES" -ge "$MIN_BACKUP_BYTES" ]; then echo "Создан: $OUTPUT ($(du --apparent-size -h "$OUTPUT" | cut -f1))" # Проверка: несжатый размер дампа (2GB БД на диске → 200–600MB SQL нормально: индексы не в дампе, потом gzip) if [ "${VERIFY_BACKUP:-0}" = "1" ]; then UNCOMPRESSED=$(gunzip -c "$OUTPUT" 2>/dev/null | wc -c) UNCOMPRESSED_MB=$(( UNCOMPRESSED / 1024 / 1024 )) echo "Несжатый размер дампа: ${UNCOMPRESSED_MB} MB (${UNCOMPRESSED} B)" TABLES_IN_DUMP=$(gunzip -c "$OUTPUT" 2>/dev/null | grep -c '^CREATE TABLE ' || true) echo "Таблиц в дампе: $TABLES_IN_DUMP" fi else echo "Ошибка: дамп пустой или слишком мал (${SIZE_BYTES} байт). Проверьте контейнер $PG_CONTAINER и пароль БД в Vaultwarden (NEXTCLOUD)." [ -s "$ERR" ] && cat "$ERR" >&2 rm -f "$OUTPUT" exit 1 fi find "$BACKUP_DIR" -name 'nextcloud-db-*.sql.gz' -mtime +$RETENTION_DAYS -delete NOTIFY_SCRIPT="${NOTIFY_SCRIPT:-/root/scripts/notify-telegram.sh}" if [ -x "$NOTIFY_SCRIPT" ]; then SIZE=$(du --apparent-size -h "$OUTPUT" | cut -f1) SIZE_BYTES=$(stat -c%s "$OUTPUT" 2>/dev/null || echo 0) BODY="Резервное копирование завершено. Объекты: дамп БД Nextcloud (PostgreSQL). Размер копии: ${SIZE}." [ "$SIZE_BYTES" -lt 10240 ] 2>/dev/null && BODY="${BODY} ⚠️ Подозрительно малый размер — проверьте контейнер nextcloud-db-1 и наличие данных в БД." "$NOTIFY_SCRIPT" "🗄️ Nextcloud (БД)" "$BODY" || true fi