From e590f541b3e494f21da82a659596e5c97350173f Mon Sep 17 00:00:00 2001 From: dmitriylaukhin Date: Wed, 8 Apr 2026 14:38:48 +0500 Subject: [PATCH] Add automated backup scripts for PostgreSQL and S3 files to Backblaze B2 Co-Authored-By: Claude Sonnet 4.6 --- scripts/backup-setup.md | 129 ++++++++++++++++++++++++++++++++++++++++ scripts/backup.sh | 70 ++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 scripts/backup-setup.md create mode 100644 scripts/backup.sh diff --git a/scripts/backup-setup.md b/scripts/backup-setup.md new file mode 100644 index 0000000..b75e817 --- /dev/null +++ b/scripts/backup-setup.md @@ -0,0 +1,129 @@ +# Настройка бекапов на сервере + +## Что бекапится +- **PostgreSQL** → дамп каждую ночь → Backblaze B2 +- **S3-файлы** (Hetzner Object Storage) → синхронизация → Backblaze B2 +- Хранение: последние 7 дневных дампов БД + все файлы (sync зеркало) + +--- + +## Шаг 1 — Backblaze B2: создать bucket и ключи + +1. Зарегистрироваться на https://www.backblaze.com/b2/ +2. **Buckets → Create a Bucket**: + - Name: `lms-backups-second-brain` + - Files in Bucket are: `Private` +3. **App Keys → Add a New Application Key**: + - Name: `lms-server` + - Access: `Read and Write` + - Bucket: `lms-backups-second-brain` + - Сохранить `keyID` и `applicationKey` — показываются один раз + +--- + +## Шаг 2 — Установить rclone на сервере + +```bash +curl https://rclone.org/install.sh | sudo bash +``` + +--- + +## Шаг 3 — Настроить rclone: Backblaze B2 + +```bash +rclone config +``` + +Ответы: +``` +n (новый remote) +name: b2lms +type: b2 +account: +key: + +q (quit) +``` + +--- + +## Шаг 4 — Настроить rclone: Hetzner S3 + +Значения берём из `.env` на сервере. + +```bash +rclone config +``` + +Ответы: +``` +n +name: hetzner +type: s3 +provider: Other +env_auth: false +access_key_id: +secret_access_key: +region: <пусто — Enter> +endpoint: + +q +``` + +Проверить: +```bash +rclone ls hetzner:lms-uploads +``` + +--- + +## Шаг 5 — Установить скрипт + +```bash +sudo mkdir -p /opt/lms-backup +sudo cp scripts/backup.sh /opt/lms-backup/backup.sh +sudo chmod +x /opt/lms-backup/backup.sh +``` + +--- + +## Шаг 6 — Настроить cron + +```bash +sudo crontab -e +``` + +Добавить строку (запуск каждую ночь в 3:00): +``` +0 3 * * * /opt/lms-backup/backup.sh >> /var/log/lms-backup.log 2>&1 +``` + +--- + +## Шаг 7 — Проверить вручную + +```bash +sudo /opt/lms-backup/backup.sh +tail -50 /var/log/lms-backup.log +``` + +--- + +## Восстановление из бекапа + +### База данных +```bash +# Скачать нужный дамп с B2 +rclone copy b2lms:lms-backups-second-brain/db/db_20260408_0300.sql.gz /tmp/ + +# Восстановить в контейнер +gunzip -c /tmp/db_20260408_0300.sql.gz \ + | docker exec -i lms-system-db-1 psql -U lms_user lms_db +``` + +### Файлы +```bash +# Синхронизировать файлы обратно на Hetzner S3 +rclone sync b2lms:lms-backups-second-brain/files hetzner:lms-uploads +``` diff --git a/scripts/backup.sh b/scripts/backup.sh new file mode 100644 index 0000000..9c9e04e --- /dev/null +++ b/scripts/backup.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# LMS Second Brain — Backup Script +# Backs up PostgreSQL (from Docker) + S3 files to Backblaze B2 +# Place at: /opt/lms-backup/backup.sh on the server +# Cron: 0 3 * * * /opt/lms-backup/backup.sh >> /var/log/lms-backup.log 2>&1 + +set -euo pipefail + +# ── Config ─────────────────────────────────────────────────────────────────── +DB_CONTAINER="lms-system-db-1" +DB_USER="lms_user" +DB_NAME="lms_db" + +BACKUP_DIR="/tmp/lms-backups" +DATE=$(date +%Y%m%d_%H%M) +DUMP_FILE="${BACKUP_DIR}/db_${DATE}.sql.gz" + +# B2 rclone remote name (configured via: rclone config) +B2_REMOTE="b2lms" +B2_BUCKET="lms-backups-second-brain" +B2_DB_PATH="${B2_REMOTE}:${B2_BUCKET}/db" +B2_FILES_PATH="${B2_REMOTE}:${B2_BUCKET}/files" + +# Hetzner S3 rclone remote name +S3_REMOTE="hetzner" +S3_BUCKET="lms-uploads" + +# Retention: keep last N daily backups +KEEP_DAYS=7 + +# ── Functions ───────────────────────────────────────────────────────────────── +log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; } + +# ── Main ────────────────────────────────────────────────────────────────────── +log "=== LMS Backup started ===" +mkdir -p "$BACKUP_DIR" + +# 1. PostgreSQL dump +log "Dumping PostgreSQL from container ${DB_CONTAINER}..." +docker exec "$DB_CONTAINER" \ + pg_dump -U "$DB_USER" "$DB_NAME" \ + | gzip > "$DUMP_FILE" +log "Dump created: ${DUMP_FILE} ($(du -sh "$DUMP_FILE" | cut -f1))" + +# 2. Upload DB dump to B2 +log "Uploading DB dump to Backblaze B2..." +rclone copy "$DUMP_FILE" "$B2_DB_PATH" +log "DB dump uploaded: ${B2_DB_PATH}/$(basename "$DUMP_FILE")" + +# 3. Sync S3 files to B2 +log "Syncing S3 files to Backblaze B2..." +rclone sync \ + "${S3_REMOTE}:${S3_BUCKET}" \ + "$B2_FILES_PATH" \ + --progress \ + --transfers=8 +log "S3 files synced to ${B2_FILES_PATH}" + +# 4. Cleanup local temp files +rm -f "$DUMP_FILE" +log "Local temp files cleaned" + +# 5. Prune old DB backups on B2 (keep last KEEP_DAYS) +log "Pruning DB backups older than ${KEEP_DAYS} days..." +rclone delete "$B2_DB_PATH" \ + --min-age "${KEEP_DAYS}d" \ + --include "db_*.sql.gz" +log "Pruning done" + +log "=== LMS Backup finished successfully ==="