---
title: "Сбои бэкапа: оповещения до того, как нужно восстановление"
description: "Бэкапы неделями ломаются молча. Ловите ошибки restic и borg, устаревшие снапшоты и пропуски запусков — и получайте звонок при сбое."
date: 2026-10-09
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - мониторинг бэкапов
  - restic
  - borg
  - оповещения о сбоях
  - звонки по алертам
  - вебхуки
---

# Оповещения о сбоях бэкапа: узнайте до того, как понадобится восстановление

Большинство людей узнаёт, что бэкапы не работали, в тот самый день, когда они нужны. Задача завершилась с ошибкой три недели назад, никто не читал лог, а свежий снапшот старше ошибки, которую вы пытаетесь отменить.

Решение — не в лучшем инструменте для бэкапа, а в том, чтобы проблема доходила до человека в тот же час, когда случилась. В этом руководстве показано, как это сделать для restic и borg (и всего, что запускается из shell): три реальных способа, которыми ломаются бэкапы, и скрипты, которые можно скопировать. На последнем шаге алерт уходит на телефон через [Echobell](/ru), так что сломанный бэкап звонит, а не лежит в почтовом ящике.

## Три способа, которыми ломается бэкап

Самая частая ошибка — следить только за первым.

1. **Запустился и упал.** Репозиторий заблокирован, диск заполнен, неверный пароль, обрыв сети. Инструмент завершается с ненулевым кодом.
2. **Запустился и выполнился наполовину.** restic завершается с кодом `3`, если снапшот создан, но часть файлов прочитать не удалось. borg возвращает `1` при предупреждениях. Оба случая легко списать со счетов, и оба могут скрывать проблему с правами доступа, из-за которой молча пропускается самый важный каталог.
3. **Не запустился вообще.** Сервер был выключен, таймер отключён, crontab перезаписан. Ошибки нет, потому что нет процесса. Именно этот сбой и убивает.

Для каждого нужен свой детектор, поэтому мы построим все три.

## Шаг 1: Создайте канал, способный привлечь внимание

В Echobell создайте канал `backups` и для важных задач выберите тип уведомления **Звонок**. Скопируйте его URL вебхука:

```
https://hook.echobell.one/t/<channel-token>
```

Задайте шаблоны, понятные спросонья, на основе полей, которые мы будем отправлять ниже:

```
Заголовок: Бэкап {{status}} на {{host}}
Текст: {{job}}: {{detail}}
```

Синтаксис описан в [справочнике по шаблонам](/ru/docs/template).

## Шаг 2: Ловите сбои скриптом-обёрткой

Небольшая обёртка превращает коды завершения в алерты. Сохраните как `/usr/local/bin/backup-run`:

```bash
#!/usr/bin/env bash
set -uo pipefail

HOOK="https://hook.echobell.one/t/<channel-token>"
JOB="nightly-restic"

alert() {
  curl -fsS -m 10 -X POST "$HOOK" \
    -H "Content-Type: application/json" \
    -d "{\"job\":\"$JOB\",\"host\":\"$(hostname)\",\"status\":\"$1\",\"detail\":\"$2\"}" || true
}

restic backup /srv /etc --exclude-caches
code=$?

case $code in
  0)  ;;                                   # успех: молчим
  3)  alert "incomplete" "Snapshot saved, but some files were unreadable" ;;
  11) alert "locked" "Repository is locked by another process" ;;
  12) alert "auth-failed" "Wrong repository password" ;;
  *)  alert "failed" "restic exited with code $code" ;;
esac

exit $code
```

Почему важны детали:

- `set -e` намеренно **не** используется, чтобы скрипт дошёл до алерта после падения restic.
- `|| true` у `curl` означает, что недоступный вебхук никогда не заменит настоящий код завершения.
- `-m 10` не даёт зависшему сетевому вызову заблокировать следующий запланированный запуск.

Коды завершения restic, использованные выше, описаны в руководстве restic: `0` — успех, `1` — фатальная ошибка, `3` — снапшот создан, но часть исходных файлов не читается, `10` — репозиторий не существует, `11` — не удалось заблокировать, `12` — неверный пароль. Сверьтесь с `restic version` на своей машине: коды добавлялись со временем.

### borg и borgmatic

borg возвращает `0` при успехе, `1` при предупреждениях и `2` и выше при ошибках. Изменение файла во время бэкапа даёт `1`, что для каталогов с логами обычное дело, поэтому распространённый подход — звонить только при `2` и выше, а для `1` отправлять тихое уведомление:

```bash
borg create --stats ::'{hostname}-{now}' /srv /etc
code=$?
if [ "$code" -ge 2 ]; then
  alert "failed" "borg exited with code $code"
elif [ "$code" -eq 1 ]; then
  alert "warning" "borg finished with warnings"
fi
```

Если вы используете borgmatic, его хук `on_error` запускает команду, когда ошибка возникает в самом borgmatic, — естественное место для того же `curl`. Учтите ограничение, которое признаёт и документация borgmatic: если borgmatic вообще не запустился, `on_error` не сработает. Это сбой номер три, и для него нужен шаг 3.

## Шаг 3: Ловите бэкап, который не запускался

Обёртка может сообщить только о запуске, который состоялся. Чтобы поймать тишину, переверните логику: бэкап отправляет ping в монитор при **успехе**, а монитор оповещает вас, когда ping **опаздывает**. Это называется dead man's switch («выключатель мертвеца»).

[Healthchecks.io](https://healthchecks.io) (облачный или собственный) делает именно это:

1. Создайте проверку с расписанием `0 3 * * *` и льготным временем в час-два.
2. Добавьте её ping в конец задачи, чтобы он срабатывал только при успехе.
3. В интеграциях проверки добавьте **Webhook** на URL вашего канала Echobell.

Ping ставится в конец обёртки:

```bash
# в backup-run прямо перед финальным `exit $code`; пингует только при успехе
if [ $code -eq 0 ]; then
  curl -fsS -m 10 --retry 3 -o /dev/null https://hc-ping.com/<your-uuid>
fi
```

Healthchecks также поддерживает варианты ping `/start` и `/fail` и умеет принимать код завершения в URL, так что одна проверка поймает и зависшие задачи. Итог: умерший сервер, отключённый таймер и удалённая строка crontab приводят к одному результату — звонит телефон. Общую схему и другие мониторы смотрите в статье [оповещения о сбоях cron-задач](/ru/blog/cron-job-failure-alerts).

## Шаг 4: Проверьте, что бэкап свежий и восстановимый

Бэкап может выполняться по расписанию, завершаться с кодом `0` и всё равно быть бесполезным — например, если после сбоя монтирования вы копируете пустой каталог. Добавьте две недорогие проверки в отдельную ежедневную задачу:

**Возраст снапшота.** Оповещайте, если самый свежий снапшот старше ожидаемого:

```bash
latest=$(restic snapshots --latest 1 --json | jq -r '.[0].time')
age_h=$(( ( $(date +%s) - $(date -d "$latest" +%s) ) / 3600 ))
if [ "$age_h" -gt 30 ]; then
  alert "stale" "Newest snapshot is ${age_h}h old"
fi
```

**Целостность репозитория.** `restic check` проверяет структуру репозитория. Запускайте его раз в неделю; для больших репозиториев `restic check --read-data-subset=5%` проверяет выборку данных, не читая всё. Оповещайте при ненулевом коде.

Ни то ни другое не заменяет настоящего тестового восстановления. Раз в квартал восстанавливайте проверяемый файл во временный каталог. Это единственный тест, который доказывает, что бэкап работает.

## Шаг 5: Звоните только когда это важно

Если каждое предупреждение звонит вам на телефон, вы заглушите канал, и он не зазвонит в тот единственный раз, когда это важно. Используйте [условия](/ru/docs/conditions) Echobell, чтобы разделить уровни в одном канале:

```
status == "failed" || status == "stale" || status == "auth-failed"
```

Отправляйте `incomplete` и `warning` во второй канал — **Срочное (Time Sensitive)** или обычный: они появятся на экране блокировки без звонка. Если хотите, чтобы дневные сбои были тихими, добавьте временное окно с помощью UTC-переменных из справочника по условиям.

## Частые вопросы

### Как получить уведомление о сбое бэкапа?

Оберните команду бэкапа, прочитайте её код завершения и отправляйте вебхук при любом ненулевом коде. Добавьте dead man's switch вроде Healthchecks.io, чтобы узнавать и о запусках, которые не начались.

### На какие коды завершения restic стоит оповещать?

Громко оповещайте на `1`, `10`, `11` и `12` — это настоящие сбои. Код `3` (снапшот создан, но часть файлов не читается) считайте предупреждением, которое нужно разобрать, поскольку он может скрывать проблему с правами.

### Как понять, что бэкап молча перестал запускаться?

Используйте heartbeat: задача отправляет ping в монитор при успехе, а монитор оповещает, когда ping опаздывает. Хук ошибки здесь не поможет: ничего не запускается, значит, и ошибку поднимать некому.

### Достаточно ли успешного кода завершения, чтобы доверять бэкапу?

Нет. Он означает лишь, что инструмент отработал, а не что данные полны или восстановимы. Проверяйте возраст снапшотов, периодически запускайте проверки целостности и регулярно тестируйте восстановление.

### Можно ли получать звонок вместо письма?

Да. Письмо легко пропустить. Звонковый канал в Echobell ведёт себя как входящий вызов и проходит через режим фокусирования iOS, поэтому сбой бэкапа в 03:00 не останется незамеченным.

## Итог

Три детектора закрывают три режима отказа: обёртка с кодами завершения — для запусков, которые падают; проверка свежести — для запусков, которые «успешны», но бесполезны; heartbeat — для запусков, которых не было. Направьте их в звонковый канал, оставьте предупреждения для более тихого — и сломанный бэкап превратится в пятиминутное исправление вместо потерянных выходных.

[Скачайте Echobell для iPhone](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-backup-failure-alerts-ru&mt=8) или [возьмите в Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid), создайте канал `backups` и запустите обёртку один раз с заведомо неверным паролем, чтобы услышать звонок.

## Похожие статьи

- [Оповещения о сбоях cron-задач](/ru/blog/cron-job-failure-alerts)
- [Руководство разработчика по усталости от алертов](/ru/blog/fix-alert-fatigue-developer-guide)
- [Справочник по условиям канала](/ru/docs/conditions)
- [Руководство по интеграции вебхуков](/ru/docs/webhook)
- [Оповещения об истечении SSL-сертификатов](/ru/blog/ssl-certificate-expiry-alerts)
