---
title: "Alertas de falha de backup: saiba antes de restaurar"
description: "Backups falham em silêncio por semanas. Detecte erros do restic e borg, snapshots velhos e execuções perdidas, e receba uma ligação."
date: 2026-10-09
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - monitoramento de backup
  - restic
  - borg
  - alertas de falha
  - alertas por ligação
  - webhooks
---

# Alertas de falha de backup: saiba antes de precisar restaurar

A maioria das pessoas descobre que o backup estava quebrado no dia em que precisa dele. O job terminou com erro três semanas atrás, ninguém leu o log, e o snapshot mais recente é anterior ao erro que você está tentando desfazer.

A solução não é uma ferramenta de backup melhor. É garantir que um problema de backup chegue a um humano na mesma hora em que acontece. Este guia mostra como fazer isso com restic e borg (e qualquer coisa que rode a partir de um shell), cobrindo as três formas como os backups realmente falham, com scripts que você pode copiar. O passo final manda o alerta para o seu celular pelo [Echobell](/pt), para que um backup quebrado toque em vez de ficar parado numa caixa de e-mail.

## As três formas como um backup falha

Monitorar só a primeira é o erro mais comum.

1. **Roda e falha.** Repositório bloqueado, disco cheio, senha errada, queda de rede. A ferramenta sai com código diferente de zero.
2. **Roda e dá certo pela metade.** O restic sai com o código `3` quando um snapshot foi criado mas alguns arquivos não puderam ser lidos. O borg sai com `1` em caso de avisos. Os dois são fáceis de ignorar, e ambos podem esconder um problema de permissão que pula em silêncio o seu diretório mais importante.
3. **Nunca roda.** O servidor estava desligado, o timer foi desativado, o crontab foi sobrescrito. Não há erro porque não há processo. É essa a falha que mais machuca.

Cada uma precisa de um detector diferente, então vamos construir os três.

## Passo 1 — Crie um canal capaz de chamar sua atenção

No Echobell, crie um canal chamado `backups` e defina o tipo de notificação como **Ligação** para os jobs que importam. Copie a URL do webhook:

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

Dê a ele templates que façam sentido meio dormindo, usando os campos que vamos enviar abaixo:

```
Título: Backup {{status}} em {{host}}
Corpo: {{job}}: {{detail}}
```

Veja a [referência de templates](/pt/docs/template) para a sintaxe.

## Passo 2 — Detecte falhas com um script wrapper

Um pequeno wrapper transforma códigos de saída em alertas. Salve como `/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)  ;;                                   # sucesso: ficar em silêncio
  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
```

Por que os detalhes importam:

- O `set -e` **não** é usado de propósito, para que o script ainda chegue ao alerta depois que o restic falhar.
- O `|| true` no `curl` faz com que um webhook inacessível nunca esconda o código de saída real.
- O `-m 10` impede que uma chamada de rede travada bloqueie a próxima execução agendada.

Os códigos de saída do restic usados acima estão documentados no manual do restic: `0` sucesso, `1` erro fatal, `3` snapshot criado mas alguns arquivos de origem ilegíveis, `10` o repositório não existe, `11` falha ao bloquear, `12` senha errada. Confirme com `restic version` na sua própria máquina, já que códigos foram sendo adicionados com o tempo.

### borg e borgmatic

O borg retorna `0` para sucesso, `1` para avisos e `2` ou mais para erros. Um arquivo que muda durante o backup gera `1`, algo rotineiro em diretórios de log, então uma abordagem comum é ligar só a partir de `2` e mandar uma notificação discreta para `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
```

Se você usa o borgmatic, o hook `on_error` dele executa um comando quando o próprio borgmatic encontra um erro, que é o lugar natural para o mesmo `curl`. Note o limite que a própria documentação do borgmatic admite: se o borgmatic nunca roda, o `on_error` nunca dispara. Essa é a falha número três, e ela precisa do Passo 3.

## Passo 3 — Detecte o backup que nunca rodou

Um wrapper só consegue relatar uma execução que aconteceu. Para pegar o silêncio, inverta a lógica: o backup envia um ping a um monitor quando **dá certo**, e o monitor avisa você quando o ping **atrasa**. Isso se chama dead man's switch.

O [Healthchecks.io](https://healthchecks.io) (hospedado ou auto-hospedado) faz exatamente isso:

1. Crie um check com o agendamento `0 3 * * *` e um tempo de tolerância de uma ou duas horas.
2. Adicione o ping dele ao final do seu job, para que só dispare em caso de sucesso.
3. Nas integrações do check, adicione um **Webhook** apontando para a URL do seu canal do Echobell.

O ping vai no final do wrapper:

```bash
# no backup-run, logo antes do `exit $code` final; só faz ping em caso de sucesso
if [ $code -eq 0 ]; then
  curl -fsS -m 10 --retry 3 -o /dev/null https://hc-ping.com/<your-uuid>
fi
```

O Healthchecks também aceita as variantes de ping `/start` e `/fail` e pode receber o código de saída na URL, então um único check também pega jobs que travam. O resultado: um servidor morto, um timer desativado e uma linha de crontab apagada produzem o mesmo desfecho, um telefone tocando. Para o padrão geral e outros monitores, veja [alertas de falha de cron jobs](/pt/blog/cron-job-failure-alerts).

## Passo 4 — Confira se o backup está atual e é restaurável

Um backup pode rodar no horário, sair com `0` e ainda assim ser inútil, por exemplo quando você está fazendo backup de um diretório vazio porque uma montagem falhou. Adicione duas checagens baratas num job diário separado:

**Idade do snapshot.** Alerte se o snapshot mais recente for mais velho do que o esperado:

```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
```

**Integridade do repositório.** O `restic check` verifica a estrutura do repositório. Rode-o semanalmente; em repositórios grandes, `restic check --read-data-subset=5%` amostra os dados sem ler tudo. Alerte em caso de saída diferente de zero.

Nenhuma das duas substitui um teste de restauração de verdade. Uma vez por trimestre, restaure para um diretório temporário um arquivo que você consiga verificar. É o único teste que prova que o backup funciona.

## Passo 5 — Tocar só quando importa

Se todo aviso te ligar, você vai silenciar o canal, e aí ele não vai tocar para o que realmente conta. Use as [condições](/pt/docs/conditions) do Echobell para separar níveis em um mesmo canal:

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

Mande `incomplete` e `warning` para um segundo canal **Urgente (Time Sensitive)** ou normal, para que apareçam na tela bloqueada sem tocar. Acrescente uma janela de horário se quiser que as falhas durante o dia sejam discretas, usando as variáveis UTC descritas na referência de condições.

## Perguntas frequentes

### Como sou avisado quando um backup falha?

Envolva o comando de backup, leia o código de saída e envie um webhook em qualquer código diferente de zero. Adicione um dead man's switch como o Healthchecks.io para também saber das execuções que nunca começaram.

### Em quais códigos de saída do restic devo alertar?

Alerte com força em `1`, `10`, `11` e `12`, que indicam uma falha real. Trate o `3` (snapshot criado mas alguns arquivos ilegíveis) como um aviso a investigar, pois ele pode esconder um problema de permissão.

### Como sei se um backup parou de rodar em silêncio?

Use um heartbeat: o job envia um ping a um monitor em caso de sucesso, e o monitor avisa quando o ping atrasa. Um hook de erro não consegue pegar isso, porque nada roda para gerar o erro.

### Um código de saída de sucesso basta para confiar num backup?

Não. Significa que a ferramenta terminou, não que os dados estejam completos ou sejam restauráveis. Confira a idade dos snapshots, rode verificações de integridade periódicas e teste uma restauração com regularidade.

### Posso receber uma ligação em vez de um e-mail?

Pode. E-mail é fácil de perder. Um canal de ligação no Echobell se comporta como uma chamada recebida e atravessa o modo Foco do iOS, então um backup que falhou às 03:00 é notado.

## Fechando

Três detectores cobrem os três modos de falha: um wrapper de códigos de saída para as execuções que falham, uma checagem de atualidade para as que dão certo sem servir para nada, e um heartbeat para as que nunca acontecem. Encaminhe-os para um canal de ligação, mantenha os avisos num mais discreto, e um backup quebrado vira um conserto de cinco minutos em vez de um fim de semana perdido.

[Baixe o Echobell para iPhone](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-backup-failure-alerts-pt&mt=8) ou [pegue no Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid), crie o canal `backups` e rode o wrapper uma vez com uma senha propositalmente errada para ouvi-lo tocar.

## Artigos relacionados

- [Alertas de falha de cron jobs](/pt/blog/cron-job-failure-alerts)
- [Guia do desenvolvedor para a fadiga de alertas](/pt/blog/fix-alert-fatigue-developer-guide)
- [Referência de condições de canal](/pt/docs/conditions)
- [Guia de integração com webhooks](/pt/docs/webhook)
- [Alertas de expiração de certificado SSL](/pt/blog/ssl-certificate-expiry-alerts)
