Testar a restauração do backup
“Temos backup” é uma esperança. “Restauramos ontem em 38 minutos e conferimos os dados” é uma garantia. Este guia monta o ensaio, mede o que importa e o transforma em rotina automática.
1. Defina o cenário e o alvo
Seção intitulada “1. Defina o cenário e o alvo”Ensaie um cenário concreto, não “restaurar o banco”. Os três que valem a pena:
- Perda total da instância — restaurar do backup mais recente.
- Erro humano às 14h32 — restaurar para 14h31 (recuperação a ponto no tempo).
- Perda da região — restaurar na região secundária.
Anote o alvo antes de começar: RTO (quanto tempo você pode levar) e RPO (quanto dado pode perder). O ensaio existe para dizer se os dois são reais.
2. Restaure em ambiente isolado
Seção intitulada “2. Restaure em ambiente isolado”Nunca ensaie por cima de produção, e nem em um ambiente que produção consulte.
# Postgres: restauração lógica em instância descartávelcreatedb -h "$HOST_TESTE" ensaio_$(date +%F)pg_restore --clean --if-exists --no-owner --jobs 4 \ --dbname "postgresql://$HOST_TESTE/ensaio_$(date +%F)" backups/loja-mais-recente.dump# RDS: PITR cria uma instância nova, sem tocar na originalaws rds restore-db-instance-to-point-in-time \ --source-db-instance-identifier loja-prod \ --target-db-instance-identifier ensaio-restore \ --restore-time 2026-09-03T17:31:00Z \ --db-subnet-group-name privado --no-publicly-accessibleCronometre desde o primeiro comando — o relógio do RTO já começou.
3. Verifique integridade, não só o exit code
Seção intitulada “3. Verifique integridade, não só o exit code”Comando que retorna zero não significa base utilizável. Verifique conteúdo:
URL="postgresql://$HOST_TESTE/ensaio_$(date +%F)"
# a base tem as tabelas esperadas?psql "$URL" -tAc "SELECT count(*) FROM information_schema.tables WHERE table_schema='public'"
# o dado mais recente — este número é o seu RPO realpsql "$URL" -tAc "SELECT max(criado_em) FROM pedidos"
# volumes por tabela crítica, comparados com a ordem de grandeza esperadapsql "$URL" -tAc "SELECT 'pedidos', count(*) FROM pedidos UNION ALL SELECT 'clientes', count(*) FROM clientes"
# integridade referencial e objetos que costumam faltarpsql "$URL" -tAc "SELECT count(*) FROM pedidos p LEFT JOIN clientes c ON c.id = p.cliente_id WHERE c.id IS NULL"psql "$URL" -tAc "SELECT count(*) FROM pg_indexes WHERE schemaname='public'"O que costuma não vir no backup e quebrar a restauração real: sequências fora de sincronia, extensões, funções, permissões de usuário, e configurações do servidor. Confira todos.
Uma verificação a mais, quando possível: aponte uma cópia da aplicação para a base restaurada e execute o fluxo principal. É o teste que prova utilidade, não apenas presença.
4. Cronometre e registre
Seção intitulada “4. Cronometre e registre”Ensaio 2026-09-04 — Postgres loja (backup lógico diário)Início: 09h02 Fim: 09h40 RTO real: 38 min (alvo: 60 min ✅)Dado mais recente: 2026-09-04 02:00 RPO real: 7h02 (alvo: 24h ✅)Verificações: 12 tabelas, contagens dentro do esperado, 0 órfãos, 34 índicesAchados: sequências precisaram de setval manual (+6 min); documentado no runbookExecutado por: @bruno (primeira vez sozinho)Registre também os achados — eles são o produto real do ensaio. RTO estimado quase nunca é o RTO medido.
5. Automatize
Seção intitulada “5. Automatize”Ensaio manual acontece duas vezes e é esquecido. Transforme em job semanal:
#!/usr/bin/env bashset -euo pipefailinicio=$(date +%s)
ULTIMO=$(ls -t backups/*.dump | head -1)pg_restore --clean --if-exists --dbname "$URL_TESTE" "$ULTIMO"
linhas=$(psql "$URL_TESTE" -tAc "SELECT count(*) FROM pedidos")recente=$(psql "$URL_TESTE" -tAc "SELECT extract(epoch from now() - max(criado_em)) FROM pedidos")[ "$linhas" -gt 0 ] || { echo "base vazia"; exit 1; }
duracao=$(( $(date +%s) - inicio ))# publique como métrica: estes dois números são o seu RTO e RPO reaiscat <<EOF | curl --data-binary @- http://pushgateway:9091/metrics/job/ensaio_restorerestore_duracao_segundos $duracaorestore_atraso_dado_segundos $recenteEOF# alerte quando o ensaio parar de rodar ou passar do alvotime() - restore_ultimo_sucesso_timestamp > 8 * 24 * 3600restore_duracao_segundos > 3600E limpe o ambiente de ensaio ao final — restauração esquecida ligada é uma das linhas mais comuns de gasto inesperado.
6. Faça o ensaio com pessoas, uma vez por ano
Seção intitulada “6. Faça o ensaio com pessoas, uma vez por ano”Automatizado prova o mecanismo. Uma vez por ano, exercite o procedimento: alguém que não é a pessoa de sempre restaura seguindo apenas o runbook, sem ajuda. Os achados aí são quase sempre acesso que falta, documento desatualizado e dependência esquecida.
Se der errado
Seção intitulada “Se der errado”| Sintoma | Causa provável |
|---|---|
pg_restore falha no meio |
Dump truncado, ou versão do servidor incompatível |
| Base restaurada sem dados recentes | O backup em si está atrasado — verifique o job |
| Aplicação não conecta na cópia | Faltaram usuários e permissões no backup lógico |
| Sequências reiniciando ids | setval não aplicado; adicione ao procedimento |
| RTO muito acima do alvo | Backup lógico em base grande — considere PITR/snapshot |
| Snapshot restaura inconsistente | Snapshot de volume sem quiesce; use o backup do banco |
Checklist de pronto
Seção intitulada “Checklist de pronto”- Cenário e alvos de RTO/RPO escritos.
- Restauração feita em ambiente isolado.
- Integridade verificada por conteúdo, não por exit code.
- Objetos auxiliares conferidos (sequências, extensões, permissões, índices).
- Tempo e atraso do dado registrados como métrica.
- Ensaio automatizado com alerta de ausência e de estouro do alvo.
- Ensaio anual com pessoas, seguindo apenas o runbook.
- Ambiente de ensaio destruído ao final.