Система резервного копирования
Скрипт
- Путь:
/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 volumes | open-webui-data | /home/claude/backups/docker-volumes/ |
| Offsite | всё выше → homepc через Tailscale | H:/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
Восстановление
- Получить passphrase из Bitwarden
gpg -d backup.tar.gz.gpg | tar xz- Для 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-07-31 ~09:31: homepc подняли в Tailscale, оффсайт догнали вручную. Не гоняли весь
-
2026-08-01: снова провал оффсайта ночью (03:34,
scp to homepc failed, тарбол 5.0G). 2-й раз за 3 дня (30-31.07 и 01.08). Утром догнал вручную.- ПРЕЖНЯЯ ГИПОТЕЗА «homepc спит ночью» — ОПРОВЕРГНУТА (была непроверенным домыслом). Факт:
LastBootUpTimehomepc = 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-таймер как страховка обсуждался, но пока не делаем — если это разовый ночной обрыв, а не паттерн, автоматика не нужна. Ручной догон утром работает.
- ПРЕЖНЯЯ ГИПОТЕЗА «homepc спит ночью» — ОПРОВЕРГНУТА (была непроверенным домыслом). Факт:
-
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 | Чем РЕАЛЬНО чистится |
|---|---|---|---|
| 1 | backup-all локально ~/backups (~5G) | 30 дней | уже есть find -mtime +30 -delete — не трогаем |
| 2 | backup-all оффсайт homepc H:/… (~150G) | 30 дней | PowerShell-retention в скрипте — не трогаем |
| 3 | restic локально на forge (новое) | keep 7d/4w/6m | Backrest: forget по политике + prune еженедельно |
| 4 | restic → Backblaze B2 (новое, оффсайт) | keep 7d/4w/6m | forget+prune + B2 lifecycle rule |
| 5 | staging 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-репо). Веб-UI127.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_ide63a6741..., proxied=false как все). Backrest подключён к сетиproxy, роут/home/claude/traefik/data/conf.d/backrest.yml→http://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/dataowned 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), endpoints3.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.
- ⚠️ TODO безопасность: присланный ключ вышел с ПОЛНЫМИ правами на аккаунт (мастер: deleteBuckets/writeKeys/deleteKeys, не ограничен на бакет). Для бэкапа избыточно — пересоздать ограниченный ключ (только
-
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: доп. слой.
- ⚠️ ГРАБЛИ БЕЗОПАСНОСТИ: Backrest по умолчанию БЕЗ авторизации — после публикации за Traefik панель
-
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-1Exited). База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 чистит остальное.
- Аудит всех БД-контейнеров forge:
-
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-allDOCKER_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). Qdrantopenclaw_v3— через нативный snapshot API ([[thoughts/ideas/inbox/1786130992327-instagram/post|POST]] /collections/openclaw_v3/snapshots→docker 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 видит шифротекст).
Грабли (не повторять)
- GPG без
--yes— со второго запуска отказывается перезаписывать существующий файл, скрипт падает сtar: Broken pipe. Всегда--yesв encrypt-функции. - PowerShell
Set-Contentломает SSH-ключи — длинные строки переносятся,authorized_keysстановится невалидным. Использовать[System.IO.File]::WriteAllTextсUTF8Encoding($false). - Windows OpenSSH + группа Administrators — ключ должен лежать в
C:\ProgramData\ssh\administrators_authorized_keys(не%USERPROFILE%\.ssh\authorized_keys), ACL толькоAdministrators:F+SYSTEM:F, inheritance disabled. - На Windows OpenSSH из коробки нет rsync — используется
scpс одним tar-файлом за день вместо rsync. - 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).