Add notification feature to backup scripts for various services

Enhance backup scripts for Nextcloud, Gitea, Paperless, Vaultwarden, Immich, and VPS configurations by adding Telegram notifications upon completion. Include details such as backup size and objects backed up. Update backup documentation to reflect these changes and ensure clarity on backup processes and retention policies.
This commit is contained in:
2026-02-27 20:42:30 +03:00
parent 56cee83198
commit f319133cee
21 changed files with 1048 additions and 168 deletions

View File

@@ -2,12 +2,15 @@
# Выгрузка /mnt/backup в Yandex Object Storage (S3) через restic (без каталога photos).
# Фото бэкапятся отдельно: backup-restic-yandex-photos.sh.
# Запускать на хосте Proxmox под root.
# Перед первым запуском: установить restic, создать /root/.restic-yandex.env и /root/.restic-password, выполнить restic init.
# Секреты: из Vaultwarden (объект RESTIC). Требуется файл с мастер-паролем: /root/.bw-master (chmod 600).
# Перед первым запуском: установить restic, bw (Bitwarden CLI), jq; bw config server https://vault.katykhin.ru; restic init.
# Cron: 0 4 * * * (04:00, после окна 01:0003:30; 05:00 зарезервировано под перезагрузку).
set -e
ENV_FILE="/root/.restic-yandex.env"
BACKUP_PATH="/mnt/backup"
# Время запуска (для логов и уведомлений)
START_TS=$(date +%s)
START_HUMAN=$(date '+%Y-%m-%d %H:%M:%S')
# Исключаем служебные каталоги и photos (фото — отдельный бэкап)
EXCLUDE_OPTS=(--exclude="$BACKUP_PATH/lost+found" --exclude="$BACKUP_PATH/photos")
@@ -16,34 +19,40 @@ if [ "$(id -u)" -ne 0 ]; then
exit 1
fi
if [ ! -f "$ENV_FILE" ]; then
echo "Нет файла $ENV_FILE. Скопируйте restic-yandex-env.example и задайте ключи."
# Секреты из Vaultwarden (объект RESTIC)
BW_MASTER_PASSWORD_FILE="${BW_MASTER_PASSWORD_FILE:-/root/.bw-master}"
if [ ! -f "$BW_MASTER_PASSWORD_FILE" ]; then
echo "Нет файла с мастер-паролем Vaultwarden: $BW_MASTER_PASSWORD_FILE (chmod 600). См. docs/backup/proxmox-phase1-backup.md"
exit 1
fi
# shellcheck source=/dev/null
source "$ENV_FILE"
if ! command -v bw >/dev/null 2>&1 || ! command -v jq >/dev/null 2>&1; then
echo "Установите bw (Bitwarden CLI) и jq для получения секретов из Vaultwarden."
exit 1
fi
export BW_SESSION
BW_SESSION=$(bw unlock --passwordfile "$BW_MASTER_PASSWORD_FILE" --raw 2>/dev/null) || { echo "Не удалось разблокировать Vaultwarden. Проверьте мастер-пароль и доступ к vault.katykhin.ru."; exit 1; }
RESTIC_ITEM=$(bw get item "RESTIC" 2>/dev/null) || { echo "Не найден объект RESTIC в Vaultwarden."; exit 1; }
export RESTIC_REPOSITORY
RESTIC_REPOSITORY=$(echo "$RESTIC_ITEM" | jq -r '.fields[] | select(.name=="RESTIC_REPOSITORY") | .value')
export AWS_ACCESS_KEY_ID
AWS_ACCESS_KEY_ID=$(echo "$RESTIC_ITEM" | jq -r '.fields[] | select(.name=="AWS_ACCESS_KEY_ID") | .value')
export AWS_SECRET_ACCESS_KEY
AWS_SECRET_ACCESS_KEY=$(echo "$RESTIC_ITEM" | jq -r '.fields[] | select(.name=="AWS_SECRET_ACCESS_KEY") | .value')
export AWS_DEFAULT_REGION
AWS_DEFAULT_REGION=$(echo "$RESTIC_ITEM" | jq -r '.fields[] | select(.name=="AWS_DEFAULT_REGION") | .value')
[ -z "$AWS_DEFAULT_REGION" ] && AWS_DEFAULT_REGION="ru-central1"
RESTIC_PASS=$(echo "$RESTIC_ITEM" | jq -r '.fields[] | select(.name=="RESTIC_BACKUP_KEY") | .value')
for var in RESTIC_REPOSITORY AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY; do
if [ -z "${!var}" ]; then
echo "В $ENV_FILE не задано: $var"
echo "В Vaultwarden (RESTIC) не задано поле для $var"
exit 1
fi
done
if [ -z "$RESTIC_PASSWORD_FILE" ]; then
RESTIC_PASSWORD_FILE="/root/.restic-password"
fi
if [ ! -f "$RESTIC_PASSWORD_FILE" ]; then
echo "Файл с паролем репозитория не найден: $RESTIC_PASSWORD_FILE. Создайте его и выполните restic init."
exit 1
fi
export AWS_ACCESS_KEY_ID
export AWS_SECRET_ACCESS_KEY
RESTIC_PASSWORD_FILE=$(mktemp -u)
echo -n "$RESTIC_PASS" > "$RESTIC_PASSWORD_FILE"
chmod 600 "$RESTIC_PASSWORD_FILE"
trap 'rm -f "$RESTIC_PASSWORD_FILE"' EXIT INT TERM
export RESTIC_PASSWORD_FILE
export RESTIC_REPOSITORY
export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-ru-central1}"
if ! command -v restic >/dev/null 2>&1; then
echo "restic не установлен. Установите: apt install restic."
@@ -51,7 +60,8 @@ if ! command -v restic >/dev/null 2>&1; then
fi
echo "Restic backup: $BACKUP_PATH (excl. photos) -> $RESTIC_REPOSITORY"
restic backup "$BACKUP_PATH" "${EXCLUDE_OPTS[@]}" --quiet
# Показываем прогресс restic (без --quiet), чтобы был виден ход бэкапа
restic backup "$BACKUP_PATH" "${EXCLUDE_OPTS[@]}"
echo "Restic forget (retention 3 daily, 2 weekly, 2 monthly)..."
restic forget --keep-daily 3 --keep-weekly 2 --keep-monthly 2 --prune --quiet
@@ -59,4 +69,42 @@ restic forget --keep-daily 3 --keep-weekly 2 --keep-monthly 2 --prune --quiet
echo "Restic prune..."
restic prune --quiet
# Время окончания и длительность
END_TS=$(date +%s)
END_HUMAN=$(date '+%Y-%m-%d %H:%M:%S')
DURATION_SEC=$(( END_TS - START_TS ))
if [ "$DURATION_SEC" -lt 0 ] 2>/dev/null; then
DURATION_SEC=0
fi
DUR_MIN=$(( DURATION_SEC / 60 ))
DUR_SEC=$(( DURATION_SEC % 60 ))
echo "Restic backup done."
echo "Время запуска: $START_HUMAN"
echo "Время завершения: $END_HUMAN"
echo "Длительность: ${DUR_MIN} мин ${DUR_SEC} сек"
# Уведомление в Telegram (шлюз тихо выходит, если конфига нет)
NOTIFY_SCRIPT="${NOTIFY_SCRIPT:-/root/scripts/notify-telegram.sh}"
if [ -x "$NOTIFY_SCRIPT" ]; then
STATS=$(restic stats latest 2>/dev/null) || true
FILES=$(echo "$STATS" | grep "Total File Count" | sed 's/.*:[[:space:]]*//')
SIZE=$(echo "$STATS" | grep "Total Size" | sed 's/.*:[[:space:]]*//')
if [ -n "$FILES" ] && [ -n "$SIZE" ]; then
BODY="Резервное копирование завершено.
Объекты: снимок /mnt/backup в Yandex (без photos). Файлов в снимке: $FILES.
Размер копии: ${SIZE}.
Время запуска: ${START_HUMAN}.
Время завершения: ${END_HUMAN}.
Длительность: ${DUR_MIN} мин ${DUR_SEC} сек."
"$NOTIFY_SCRIPT" "☁️ Restic Yandex" "$BODY" || true
else
BODY="Резервное копирование завершено.
Объекты: снимок /mnt/backup в Yandex (без photos).
Размер копии: — (stats недоступны).
Время запуска: ${START_HUMAN}.
Время завершения: ${END_HUMAN}.
Длительность: ${DUR_MIN} мин ${DUR_SEC} сек."
"$NOTIFY_SCRIPT" "☁️ Restic Yandex" "$BODY" || true
fi
fi