Система резервного копирования

Скрипт

  • Путь: /home/claude/bin/backup-all.sh
  • Расписание: ежедневно 03:30 (systemd timer)
  • Шифрование: GPG symmetric, AES256
  • Passphrase: в Bitwarden

Что бекапится

ТипЧтоКуда
Проекты (корень)все ~/*/ кроме excluded/home/claude/backups/projects/<name>/
Проекты (projects/)все ~/projects/*//home/claude/backups/projects/projects__<name>/
Postgresвсе БД в coreclaw-postgres/home/claude/backups/postgres/
Dotfiles.config/systemd, ~/.claude, bin, CLAUDE.md и др./home/claude/backups/home/
Docker volumesopen-webui-data/home/claude/backups/docker-volumes/
Offsiteвсё выше → homepc через TailscaleH:/Backups/forge/

Исключённые директории

backups, coreclaw-workspace, restore, projects (сканируется отдельно через projects/*/)

Исключения из tar

.venv, node_modules, __pycache__, *.pyc, .git
logs, *.log, dist, build, .next, .cache
tmp, *.tmp, qdrant-data  ← qdrant данные не нужны (переиндексируем из vault)

Исключённые директории (не бекапятся)

backups, coreclaw-workspace, restore

Восстановление

  1. Получить passphrase из Bitwarden
  2. gpg -d backup.tar.gz.gpg | tar xz
  3. Для Qdrant: запустить systemctl --user restart dbrain-rag-indexer — переиндексирует vault

Мониторинг

  • Marker file: /home/claude/backups/.last-success
  • Лог: /home/claude/backups/backup.log
  • Retention: 30 дней
  • homepc retention: 30 дней (PowerShell скрипт)
  • Оффсайт: scp тарбола на homepc (sergmak@100.108.187.92) через Tailscale DERP, ключ /home/claude/.ssh/backup-homepc (ed25519, без passphrase), назначение H:\Backups\[[projects/forge]]\[[projects/forge]]-YYYY-MM-DD.tar

Инциденты

  • 2026-07-30 и 07-31: оффсайт падает 2 ночи подряд (ERROR: scp to homepc failed, connection timed out). Локальные бэкапы при этом целые и свежие (проекты, vault, 5 Postgres-баз, dotfiles; ~4.9G). Причина — homepc offline в Tailscale (last seen ~1 день, порт 22 timeout, комп выключен/спит или Tailscale не поднят). Forge собирает тарбол нормально (4.8G), отправить некуда. До 29.07 включительно оффсайт был OK. Зависимость: свежесть оффсайт-копии = homepc в сети Tailscale. Фикс: разбудить homepc + поднять Tailscale, затем прогнать оффсайт вручную (backup-all.sh или scp последнего тарбола), не дожидаясь ночного крона. .last-success обновляется только при успешном оффсайте, поэтому маркер отстаёт на время простоя homepc.

    • РЕШЕНО 2026-07-31 ~09:31: homepc подняли в Tailscale, оффсайт догнали вручную. Не гоняли весь backup-all.sh — локальные бэкапы за 03:32 были целы, повторили только оффсайт-шаг: tar cf /tmp/[[projects/forge]]-<DATE>.tar projects postgres docker-volumes home из $BACKUP_ROOT → scp на homepc → touch .last-success. Проверено на homepc: H:\Backups\[[projects/forge]]\[[projects/forge]]-2026-07-31.tar, 4.78 GB. Рецепт догона оффсайта на будущее — этот же (тарбол собирается из уже готовых .gpg, не пересобирая бэкапы).
  • 2026-08-01: снова провал оффсайта ночью (03:34, scp to homepc failed, тарбол 5.0G). 2-й раз за 3 дня (30-31.07 и 01.08). Утром догнал вручную.

    • ПРЕЖНЯЯ ГИПОТЕЗА «homepc спит ночью» — ОПРОВЕРГНУТА (была непроверенным домыслом). Факт: LastBootUpTime homepc = 30.07 11:48, uptime ~2 дня, включая обе провальные ночи, событий сна нет → комп ночью работал, не спал.
    • Версия «засыпает сетевая карта» — НЕ подтверждена, вероятно неверна. Хотя у физического Ethernet (Realtek PCIe GbE) AllowComputerToTurnOffDevice = Enabled, эта версия не выдерживает двух фактов: (1) провалы начались только с ночи 30→31.07, а 27-29.07 оффсайт шёл штатно — если бы NIC засыпал в простое, падало бы всегда; (2) powercfg -waketimers = 0 (никто карту не будит), при этом утром комп в сети без участия Сергея, а мои ночные scp-пакеты шли на тот же TS-IP, что и утренние ping — но ночью карту «не разбудили». Рамка «уснула карта → пакет будит» неверна.
    • Что достоверно из данных homepc: Event 6008 = аварийное завершение работы, старт системы 30.07 11:48, uptime с тех пор ~2 дня (комп ночью НЕ спал). Провалы бэкапа начались ровно после этого рестарта. → Причина в чём-то, что изменилось/не поднялось после аварийного выключения 30.07, а не в питании NIC.
    • ПРИЧИНА НАЙДЕНА (со слов Сергея, 01.08): ночью не было интернета дома. Это исчерпывающе объясняет всё: 27-29.07 работало (связь была), карту никто не будил (NIC ни при чём), Tailscale-путь «рвался» потому что канала не было вообще. Ни power-save NIC, ни провайдерский реконнект — просто ночью лежал домашний интернет. Обе мои гипотезы (NIC, потом Tailscale endpoint) — мимо.
    • Урок: не строить причинных версий (NIC, Tailscale), пока не исключён самый банальный слой — наличие связи на той стороне. Оба вопроса Сергея («почему раньше работало», «кто разбудил карту») сразу вели к «а был ли ночью интернет», а не к железу.
    • Решение: оставлено в режиме наблюдения (по решению Сергея). Catch-up-таймер как страховка обсуждался, но пока не делаем — если это разовый ночной обрыв, а не паттерн, автоматика не нужна. Ручной догон утром работает.
  • 2026-08-19 и 08-20: оффсайт снова падает 2 ночи подряд (scp to homepc failed, timeout на 100.108.187.92:22). Тот же паттерн, что 30-31.07: локальные бэкапы (проекты, 5 Postgres, dotfiles, ~5.6G) целые каждую ночь, .last-success не обновлялся с 18.08. Утром 20.08 homepc в Tailscale offline, last seen ~1 день назад — комп/интернет дома лежит дольше одной ночи. Не догонялось руками. Наблюдение: обрывы уже не единичные — 2 инцидента (30-31.07, 19-20.08) по 2+ ночи подряд каждый, стоит пересмотреть «режим наблюдения», если участится.

Git-аудит проектов (2026-07-31) — дыра из-за exclude .git

.git исключён из tar → git-история проектов без remote в бэкап НЕ попадает (рабочие файлы попадают, только текущий срез). Аудит всех ~/* и ~/projects/*:

  • Все git-проекты с remote запушены (coreclaw, vpnbot-v3, ccbot, claudeclaw, savereelsbot, forge, jarvis-web, gwmcp, github-mcp; iva — detached HEAD на теге v0.3.0, но main в origin, 0 dirty; quartz — 1 незакоммич. рабочий файл, в бэкапе есть).
  • football-manager — был БЕЗ git/remote вообще → закрыто 31.07: запушен приватным на github.com/Kobalt695/[[projects/football-manager]].
  • ~/bin (12 инфраскриптов: backup-all.sh, coreclaw-backup, deploy-coreclaw, add-swap…) — НЕ в git. ⚠️ Содержат хардкод Telegram bot-токена (notify-tg.sh, subs-alert.sh, токен 8648058964:...) — пушить как есть нельзя, сперва вынести секрет в ~/.claude/secrets/ + source. Пока живут только в бэкапе (текущий срез).
  • docker-обвязки без git (local-rerank, silero-tts, cloudcli, inventory-importer) — в бэкапе как файлы, низкий приоритет.
  • Вывод: пока бэкап (forge + оффсайт homepc) цел — потеря данных ~0, т.к. файлы всех проектов бэкапятся. .git-дыра бьёт только по истории проектов без remote.

План: переход на Backrest/restic (составлен 05.08, статус — planned)

Мотив: текущий backup-all.sh делает полные tar+gpg каждый день (раздувание ~5G локально + ~150G на homepc за 30 копий), «достать 1 файл» = распаковать весь тарбол, оффсайт завязан на homepc (падает, когда комп спит). Backrest (веб-UI для restic) решает все три: дедупликация+инкрементальность, нативное шифрование, point-restore одного файла из снапшота через веб, облачный оффсайт без homepc.

Ключевой факт restic: forget только помечает снапшоты, место освобождает ТОЛЬКО restic prune. Без prune на расписании репозиторий растёт вечно — это и есть «уборка», которую надо явно ставить.

Решения Сергея (05.08): этап 1 — оба движка параллельно (бэкап важен, не ломаем старое); оффсайт — оба (локальный restic на forge + облако). Backrest ставим на Forge.

Карта хранилищ после этапа 1 (5 штук) и их уборка

#ХранилищеRetentionЧем РЕАЛЬНО чистится
1backup-all локально ~/backups (~5G)30 днейуже есть find -mtime +30 -delete — не трогаем
2backup-all оффсайт homepc H:/… (~150G)30 днейPowerShell-retention в скрипте — не трогаем
3restic локально на forge (новое)keep 7d/4w/6mBackrest: forget по политике + prune еженедельно
4restic → Backblaze B2 (новое, оффсайт)keep 7d/4w/6mforget+prune + B2 lifecycle rule
5staging pg_dump (5 баз перед снапшотом)pre-hook пишет в фиксированные имена (overwrite) — не растёт

Два скрытых мусора:

  • B2 хранит удалённое как «версии» → после prune объекты висят, счёт растёт незаметно. Фикс: bucket lifecycle «keep only last version / delete hidden after 1 day» (один раз при создании бакета).
  • pg_dump staging: писать дампы в ФИКСИРОВАННЫЕ имена (перезапись), не с датой — иначе накопление.

Что удаляем на этапе 2 (после того как restic доказал себя, отказ от backup-all) — уборка ~155G

  • 🗑️ ~/backups целиком (~5G на forge)
  • 🗑️ H:/Backups/[[projects/forge]]/*.tar на homepc (~150G) — заменяются дедупл. restic
  • 🗑️ backup-all.sh + его systemd-timer + homepc scp-ключ (если homepc-оффсайт не нужен, есть B2)
  • 🗑️ GPG-passphrase из Bitwarden (restic свой пароль репо)
  • Итог: старая схема освобождает ~155G, restic-репо ~10–20G (дедупл).

✅ ЭТАП 2 ВЫПОЛНЕН 21.08.2026: backup-all.timer/backup-all.service отключены (systemctl --user disable --now). ~/backups на forge (5.5G) удалён. H:/Backups/[[projects/forge]]/*.tar на homepc (26 файлов, 128.5G) удалены. Единственный бэкап теперь — restic (local + B2), автопрогон 04:00. backup-all.sh и scp-ключ ~/.ssh/backup-homepc пока не удалены физически (оставлены на диске, но не используются) — GPG-passphrase из Bitwarden не тронут, можно почистить отдельно, не срочно.

Фазы

  • Ф1 — Backrest на forge: docker + веб-UI за Traefik (backrest.08317.ru + auth). Два репо: локальный (forge) + B2. Пароль репо → ~/.claude/secrets/ (в vault только путь). Нужен Сергей: регистрация Backblaze B2 + application key.
  • Ф2 — источники+расписание: те же данные и исключения (node_modules/.venv/.git/.quartz/…); pg_dump pre-hook (5 баз в staging); расписание 03:30; retention 7d/4w/6m; prune еженедельно; B2 lifecycle-rule.
  • Ф3 — проверка: тестовый point-restore одного файла; неделя параллельно с backup-all; сверка; убедиться что B2-оффсайт уходит без homepc.
  • Ф4 — переключение: отключить backup-all, выполнить уборку ~155G из таблицы выше.

Прогресс

  • 05.08 — Backrest поднят локально (Ф1 частично): docker на Forge, /home/claude/backrest/ (compose + data/ config + cache/ + repo/ локальный restic-репо). Веб-UI 127.0.0.1:9898 (HTTP 200), пока НЕ наружу. /home/claude смонтирован read-only как /userdata. Пароль restic-репо → ~/.claude/secrets/backrest-restic.txt (в vault только путь). Управление: cd ~/backrest && docker compose ps/logs.

  • 05.08 — ОПУБЛИКОВАН за Traefik: https://backrest.08317.ru (HTTPS, валидный cert). Wildcard-DNS нет → A-запись backrest.08317.ru → 82.39.215.94 создана программно через Cloudflare API (токен CF_DNS_API_TOKEN взят из /home/claude/traefik/.env, тот же что Traefik юзает для DNS-01; zone_id e63a6741..., proxied=false как все). Backrest подключён к сети proxy, роут /home/claude/traefik/data/conf.d/backrest.ymlhttp://backrest:9898. Backrest имеет собственную auth (логин создаётся при первом входе).

  • 05.08 — первый снапшот сделан, РАЗМЕР ИЗМЕРЕН: локальный restic-репо /home/claude/backrest/repo инициализирован, первый снапшот (те же источники/исключения, что backup-all + home-кэши/backups исключены). Итог: 3.24 GiB исходных → ~1.1 GiB в репозитории (сжатие+дедуп). Это = размер оффсайта в S3 (restic-репо идентичен на любом бэкенде). Дальше только дельта-инкременты; с retention+prune устаканится ~1.5–2.5 GiB. Против текущего backup-all (~5G локально + ~150G на homepc) — колоссальная экономия, в S3 по сути бесплатно (<10G free tier B2 / центы на РФ-S3). Нюанс: repo/data owned by root (restic в контейнере от root) — du от claude его не видит, это норма; данные персистентны на хосте.

  • Диск forge под давлением: 89% занято, 17G свободно — под локальный restic-репо (~1.1G) на старте ок, при полном переезде освободить убрав ~/backups (~5G) и старый backup-all.

  • Оффсайт — развилка по оплате: Backblaze B2 ($6/TB/мес, 10G free, но РФ-карта не пройдёт — западный сервис). Альтернатива с РФ-оплатой: S3-совместимые Selectel/Timeweb/Yandex Object Storage (restic s3:-бэкенд, данные шифруются restic до отправки → провайдер видит шифротекст). Или без облака: restic-SFTP на homepc (бесплатно, но зависимость от homepc). Решение Сергея — ожидается.

  • 06.08 — оффсайт B2 подключён: Backblaze B2, регион EU Central, бакет forge-backup-kb695 (allPrivate), endpoint s3.eu-central-003.backblazeb2.com. restic S3-репо инициализирован (тот же пароль, что локальный). Ключи (keyID/applicationKey) → ~/.claude/secrets/backrest-restic.txt. Первая заливка restic copy локальный→B2 завершена (1:18, snapshot 80fa9f6c, restic check → no errors, целостность OK). B2 lifecycle rule выставлен (daysFromHidingToDeleting:1) — удалённые prune’ом объекты физически стираются через день, скрытый B2-мусор закрыт. Free tier 10G >> наши ~1.1G → бесплатно.

    • ⚠️ TODO безопасность: присланный ключ вышел с ПОЛНЫМИ правами на аккаунт (мастер: deleteBuckets/writeKeys/deleteKeys, не ограничен на бакет). Для бэкапа избыточно — пересоздать ограниченный ключ (только listFiles/readFiles/writeFiles/deleteFiles на [[projects/forge]]-backup-kb695), заменить в secrets.
    • Схема оффсайта: локальный restic-репо на forge → restic copy в B2 (два репо, одинаковый пароль). Env для B2: RESTIC_REPOSITORY=s3:https://s3.eu-central-003.backblazeb2.com/forge-backup-kb695, AWS_ACCESS_KEY_ID=keyID, AWS_SECRET_ACCESS_KEY=applicationKey.
  • 06.08 — АРХИТЕКТУРА финализирована: движок на systemd+restic, Backrest = GUI-просмотрщик. Backrest хранит конфиг в БД (/data/kvdb.sqlite, в volume), НЕ в config.json → программно через файл не настроить, только UI/API (API требует пароль). Поэтому движок бэкапа вынесен в скрипт /home/claude/bin/restic-backup.sh + systemd-таймер (прозрачно, версионируемо, независимо от Backrest UI). Backrest подключается к тем же двум репо (local + B2) как просмотрщик/restore с компа. Скрипт: pg_dump 5 баз (coreclaw/inventory/tg_secretary/n8n через -U [[projects/coreclaw]]; remnawave_dev через свой $POSTGRES_USER) в ~/.backup-staging (фикс. имена) → снапшот /userdata в local → forget+prune (7d/4w/6m) → restic copy в B2 → forget+prune B2. restic берётся из контейнера backrest (docker exec backrest restic, v0.19.1). Секреты читаются из ~/.claude/secrets/backrest-restic.txt.

    • ⚠️ ГРАБЛИ БЕЗОПАСНОСТИ: Backrest по умолчанию БЕЗ авторизации — после публикации за Traefik панель backrest.08317.ru какое-то время висела открытой в интернете (Сергей включил логин/пароль в настройках 06.08). Окно короткое, домен свежий, снапшоты всё равно зашифрованы restic-паролем → риск невысокий, но на будущее: публиковать Backrest ТОЛЬКО с сразу включённым auth, лучше + доп. слой (Cloudflare Access / Traefik basic-auth). TODO: доп. слой.
  • 06.08 — ДВИЖОК ГОТОВ И АВТОМАТИЗИРОВАН (Ф2+Ф3 сделаны):

    • restic-backup.sh протестирован end-to-end: 5 pg_dump OK, снапшот инкрементальный (2-й прогон: обработано 3.25 GiB, добавлено только 46 MiB — дедуп работает), forget+prune local, copy→B2, forget+prune B2 — весь цикл 28 сек.
    • systemd-таймер restic-backup.timer — ежедневно 04:00 (после старого backup-all в 03:30), Persistent=true, Linger=yes (переживёт логаут/ребут). Маркер ~/backrest/.last-success, лог ~/backrest/restic-backup.log.
    • Point-restore проверен: restic dump latest /userdata/CLAUDE.md → файл идентичен оригиналу, без распаковки всего бэкапа. «Достать 1 файл» работает.
    • Итог Ф1-Ф3: оба репо (local ~1.1G + B2 EU оффсайт) живут, обновляются сами в 04:00, дедуп+инкремент, retention 7d/4w/6m+prune, B2 lifecycle чистит. Оффсайт НЕ зависит от homepc. Осталось: этап 2 (после недели проверки — отключить backup-all + уборка ~155G), + опц. ограниченный B2-ключ, доп. auth-слой Backrest, подключить репо в Backrest UI (просмотр с компа).
  • 06.08 — Backrest UI подключён к обоим репо (просмотрщик готов): оба репо добавлены в Backrest как existing[[projects/forge]]-local (/repos/local) и [[projects/forge]]-b2 (s3:https://s3.eu-central-003.backblazeb2.com/forge-backup-kb695 + AWS-ключи в env). Настройки репо: Shared=ON (Backrest не делает свой retention/prune — этим рулит systemd-скрипт), Auto Unlock=OFF (чтобы не снёс lock активного ночного бэкапа). Все Backrest-расписания/cron ВЫКЛЮЧЕНЫ (бэкапит systemd-таймер, Backrest — только просмотр/restore). Грабли UI: снапшоты existing-репо не появляются в дереве сами — нужна кнопка «Index Snapshots» (особенно для S3/B2; для local подхватились сразу). Auth Backrest включён (логин/пароль у Сергея). Этап 1 полностью завершён.

  • 06.08 — аудит БД + Telegram-алерт добавлены:

    • Аудит всех БД-контейнеров forge: [[projects/coreclaw]]-postgres (coreclaw/tg_secretary/inventory/n8n), remnawave-db-dev (remnawave_dev), remnawave-redis-dev (Valkey-кэш). Бэкапим все 5 пользовательских баз запущенных контейнеров ✓. Системные postgres не нужны, Valkey — эфемерный кэш (не бэкапим). Prod-remnawave на 193.42.124.166 — другая инфра, не в scope forge-бэкапа.
    • ⚠️ НАЙДЕНА дыра (06.08): dev-стек vpnbot-v3 остановлен ~7 недель (контейнеры vpnbot-v3-{bot,db,adminer,redis}-dev-1 Exited). База vpnbot-v3-db-dev-1 (postgres:16, порт 5435) ВНЕ бэкапа — pg_dump берёт только запущенные контейнеры, а этот down (данные в docker volume). Открытые вопросы (ждут Сергея): dev-стек заброшен намеренно или поднять? Данные ценные → бэкапить (поднять контейнер / дампить volume напрямую), dev-мусор → забыть. NB: на forge ДВА VPN-стека dev — панель remnawave (remnawave_dev, работает, бэкапится) vs сам бот vpnbot-v3 (своя БД, остановлен, вне бэкапа).
    • Telegram-алерт на провал в restic-backup.sh (через notify-tg.sh → CoreClaw бот, chat 30777924). Покрывает: снапшот local, pg_dump, copy→B2 оффсайт (copy_rc). Успех тихий (молчание = ок, сообщение = проблема — паттерн, чтобы оффсайт не падал молча как было с homepc-scp). Проверено тестовым сообщением 06.08.
    • Retention (уже было): --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune на обоих репо = глубина истории до полугода (7 дневных + 4 недельных + 6 месячных), prune чистит остальное.
  • 06.08 — ПОЛНЫЙ DR-аудит проектов + docker-volumes (важно). Граница бэкапа: данные в /home/claude попадают в снапшот файлово, данные в docker-volumes (/var/lib/docker) — НЕТ.

    • Бэкапятся ✅/home/claude): Hermes (5 SQLite в .hermes/: state/kanban/response_store/verification_evidence/cron-executions, 2.4G — файлово, но SQLite при живой записи чуть неконсистентен, идеал = .backup), iva (.eve/ 50M), Jarvis (своей БД нет — юзает базу [[infra/inventory]]), CoreClaw postgres [[projects/coreclaw]].
    • НЕ бэкапятся ❌ (docker-volumes вне снапшота): vpnbot_dev_pgdata (63M, база бота — потеря), [[projects/coreclaw]]-qdrant-data (39M) — содержит коллекцию openclaw_v3 = ВТОРАЯ ПАМЯТЬ CoreClaw, уникальна, НЕ reindex-able из vault (коллекция vault — reindex-able, не жалко), gwmcp_credentials (Google-креды, перевыпускаемо), telegram-bot-api-data (100K, сессии). Общая причина: backup-all DOCKER_VOLUMES массив ПУСТ, restic берёт только /home/claude + pg_dump живых → volumes нигде.
    • Прирост от volume-бэкапа: сырьё ~102M (vpnbot 63 + qdrant 39 + мелочь) → ~50-60M в репо (+~5% к 1.1G), на B2 free 10G незаметно. Qdrant лучше через нативный snapshot API (tar меняющихся данных плохо дедуплицируется).
    • РЕАЛИЗОВАНО 06.08 — volume-дыра закрыта: в restic-backup.sh добавлен шаг 1b. Docker-volumes через docker run --rm -v <vol>:/vol:ro alpine tar czf в ~/.backup-staging/volumes/ (фикс. имена): vpnbot-pgdata.tar.gz (7.7M сжато), telegram-bot-api.tar.gz (56K), gwmcp-credentials.tar.gz (4K). Qdrant openclaw_v3 — через нативный snapshot API ([[thoughts/ideas/inbox/1786130992327-instagram/post|POST]] /collections/openclaw_v3/snapshotsdocker cp из [[projects/coreclaw]]-qdrant~/.backup-staging/qdrant/openclaw_v3.snapshot (1.5M) → DELETE snapshot в qdrant, чтобы не копить). vault-коллекция qdrant НЕ бэкапится (reindex-able). Всё это в staging → попадает в снапшот. Провалы volume/qdrant тоже шлют Telegram-алерт (dump_ok). Проверено: прирост репо +55 MiB (≈+5%), файлы подтверждены в снапшоте (restic ls latest). Переезд теперь восстанавливает данные всех проектов, не только код+файлы.
  • 21.08 — проверка перед этапом 2: copy→B2 идёт чисто каждую ночь без единого сбоя с 06.08 (15+ дней, план «неделя» перевыполнен), последний прогон 21.08 04:00:49 OK. Параллельно старый homepc-оффсайт падал дважды за то же время (30-31.07, 19-20.08) — restic/B2 не зависит от homepc и НЕ падал ни разу. Решение: пора на этап 2 (отключить backup-all.sh+timer+homepc-ключ, уборка ~155G) — подтверждение удаления данных запрошено у Сергея.

  • 07-08.08 — ночные автопрогоны идут чисто (04:00, ~40 сек, алертов нет). Наблюдение: репо raw стабильно ~1.275 GiB на 4 снапшота (added≈removed каждый прогон → prune держит, не растёт линейно). TODO-оптимизация на этап 2: pg_dump.sql.gz / tar-volumes / qdrant-snapshot недетерминированы (gzip байтово меняется даже при тех же данных → restic пересохраняет целиком каждую ночь, ~196M «оборот»). Фикс — gzip --rsyncable (детерм. блоки) или дампить БЕЗ сжатия (restic сам сожмёт+дедуплицирует текстовый SQL поблочно). Не срочно (репо в норме, B2 10G с запасом), докинуть вместе с уборкой этапа 2.

Риски

  • Postgres через файловый снапшот без дампа = битый бэкап → обязателен pg_dump pre-hook (Ф2, реализован в restic-backup.sh).
  • restic-репо защищён паролем — потеря пароля = потеря всего. Пароль в secrets + отдельно у Сергея.
  • B2 = данные у стороннего провайдера (но зашифрованы restic до отправки — B2 видит шифротекст).

Грабли (не повторять)

  1. GPG без --yes — со второго запуска отказывается перезаписывать существующий файл, скрипт падает с tar: Broken pipe. Всегда --yes в encrypt-функции.
  2. PowerShell Set-Content ломает SSH-ключи — длинные строки переносятся, authorized_keys становится невалидным. Использовать [System.IO.File]::WriteAllText с UTF8Encoding($false).
  3. Windows OpenSSH + группа Administrators — ключ должен лежать в C:\ProgramData\ssh\administrators_authorized_keys (не %USERPROFILE%\.ssh\authorized_keys), ACL только Administrators:F + SYSTEM:F, inheritance disabled.
  4. На Windows OpenSSH из коробки нет rsync — используется scp с одним tar-файлом за день вместо rsync.
  5. Systemd user-сервис не видит новую группу docker — даже если id показывает claude в группе docker, systemd user manager инициализируется с группами login-сессии на момент старта и не подхватывает новые без ре-логина/ребута. Симптом: permission denied while trying to connect to the docker API. Фикс: ExecStart=/usr/bin/sg docker -c '<команда>'sg переключает group context для всего процесса без ребута сессии (тот же паттерн позже пригодился для [[projects/claudeclaw]].service, см. claudeclaw).