backupally storage

Gerencia e verifica backups armazenados no storage de uma task.

Sintaxe

backupally storage <SUBCOMANDO> [OPÇÕES]

Subcomandos

Subcomando Descrição
list Listar backups no storage
info Informações do storage (totais, espaço)
verify Verificar integridade de backups
check Verificar backup específico
download Baixa um backup para um diretório local (desde 0.35.0)
registry Cadastro de destinos reutilizáveis (desde 0.26.0)

storage list

Lista todos os backups armazenados no storage de uma task.

backupally storage list <TASK_ID> [OPÇÕES]

Opções:

Opção Descrição Padrão
-f, --format <FORMAT> Formato de saída: text, json text
-i, --index <INDEX> Índice do storage (se task tem múltiplos) 0
--checksums Mostrar checksums remotos false
--database <NOME> Só este banco (tasks com ${database_name} no path) — desde 0.31.0 -
--limit <N> Máximo de arquivos, mais recentes primeiro — desde 0.31.0 -

Tasks por banco (path = ".../${database_name}", mysql/postgres com database = "*"): desde a 0.31.0 o list lista a partir do prefixo antes da variável e desce nos subdiretórios, agrupando por banco. Antes disso ele listava o path com o placeholder e respondia Nenhum backup encontrado para uma task com dezenas de backups diários (#15).

📦 Backups por banco em 'postgres' (b2: borlot-backups/production-postgres)

  clientes                            7 arquivos     312.4 MB   último 2026-09-10 03:02
  faturamento                         7 arquivos      48.1 MB   último 2026-09-10 03:02

  Total: 2 bancos, 14 arquivos, 360.5 MB

-o json devolve cada arquivo com o campo subdir (= banco).

Exemplo:

# Listar backups
backupally storage list mysql-prod

# Só um banco, últimos 3
backupally storage list postgres --database clientes --limit 3

# Listar com checksums
backupally storage list mysql-prod --checksums

# Listar em JSON
backupally storage list mysql-prod --format json

Saída:

📦 Backups no storage de 'mysql-prod' (b2: my-bucket/mysql/)

  ARQUIVO                                    TAMANHO    DATA
  mysql-prod_20260212_020000.ally           15.24 MB   2026-02-12 02:00
  mysql-prod_20260211_020000.ally           14.87 MB   2026-02-11 02:00
  mysql-prod_20260210_020000.ally           15.12 MB   2026-02-10 02:00

  Total: 3 arquivos, 45.23 MB

storage info

Exibe informações resumidas do storage de uma task. Em task por banco (${database_name} no path) traz os totais por banco e o geral; aceita --database <NOME> (0.31.0).

backupally storage info <TASK_ID> [OPÇÕES]

Opções:

Opção Descrição Padrão
-i, --index <INDEX> Índice do storage 0

Exemplo:

backupally storage info mysql-prod

Saída:

📊 Informações do Storage para 'mysql-prod'

   Driver:    b2
   Bucket:    my-bucket
   Prefixo:   mysql/

   Arquivos:  45
   Tamanho:   687.32 MB
   Mais antigo: mysql-prod_20260101_020000.ally
   Mais recente: mysql-prod_20260212_020000.ally

storage check

Verifica se um backup específico existe e está acessível.

backupally storage check <TASK_ID> <FILENAME> [OPÇÕES]

Opções:

Opção Descrição Padrão
-i, --index <INDEX> Índice do storage 0

Exemplo:

backupally storage check mysql-prod mysql-prod_20260212_020000.ally

Saída:

🔍 Verificação de 'mysql-prod_20260212_020000.ally'

   Existe:    ✅ Sim
   Legível:   ✅ Sim
   Tamanho:   15.24 MB
   Checksum:  a1b2c3d4e5f6...

✅ Backup OK

storage download

Baixa um backup do storage (local/B2/S3) para um diretório local — desde a 0.35.0 (feature storage.download em `--capabilities`).

backupally storage download <TASK_ID> <KEY> --to <DIR> [OPÇÕES]

Opções:

Opção Descrição
--to <DIR> Diretório de destino (criado se não existir) — obrigatório
--storage <ID> Storage da task (padrão: o primeiro)
--database <NOME> Banco, para task com ${database_name} no path
--overwrite Sobrescreve arquivo existente no destino

<KEY> é o artifacts[].remote_path do `history` — resolve em qualquer layout de gravação (prefixo, ${hostname}, ${date}...). A artifacts[].key antiga (= filename do storage list) continua aceita quando resolve sozinha, sem prefixo nem variável.

Exemplo:

backupally history -o json --task mysql-prod --limit 1 \
  | jq -r '.data.items[0].artifacts[0].remote_path'
# mysql-prod/mysql-prod_20260919_030000.ally

backupally storage download mysql-prod \
  mysql-prod/mysql-prod_20260919_030000.ally \
  --to /tmp/restaurar --overwrite -o json

O que acontece:

  • Grava num arquivo .part irmão do destino e só renomeia depois do download terminar íntegro — nunca sobrescreve o destino antes de terminar, nem deixa .part nem arquivo final se a conexão cair no meio;
  • Integridade: depois de baixar, calcula o SHA-256 e compara com artifacts[].sha256 da run mais recente do history que tem essa key. Divergiu: apaga o arquivo baixado e falha com E4004 — nada fica no destino. Sem checksum no history (log anterior à 0.35.0): baixa e devolve integrity_verified:false, sem conferir;
  • Download de B2/S3 é em streaming, sem teto de tempo total no corpo da resposta — cada leitura individual tem prazo próprio (120s), então conexão que para de mandar bytes sem fechar vira erro em vez de pendurar indefinidamente. Só a abertura da conexão tem prazo fixo (connect_timeout, 30s);
  • Destino já existente sem --overwrite: recusa (E1002). Com --overwrite, só troca o arquivo bom depois do download terminar e conferir — nunca apaga o arquivo antigo antes de ter o novo pronto.

storage.download (-o json):

Campo Tipo Nota
task_id string
storage_id string? resolvido
key string como foi passada
path string? arquivo final (só em sucesso)
bytes int? só em sucesso
remote_path string? presente quando a key foi achada a partir da raiz do storage
sha256 string? SHA-256 do arquivo baixado (só em sucesso)
integrity_verified bool? true: conferido contra o history; false: o history não tinha checksum para essa key

Ver Segurança e Criptografia para as limitações de contenção do storage local, e Restaurar Backups para os códigos de erro completos e para restore --from-storage, que baixa e restaura num único comando.

storage verify

Verifica a integridade de um ou mais backups.

backupally storage verify <TASK_ID> [OPÇÕES]

Opções:

Opção Descrição Padrão
-f, --file <FILE> Verificar arquivo específico Todos
-i, --index <INDEX> Índice do storage 0
--deep Verificação profunda (valida header .ally) false
--check-history Cruzar com histórico de execução false

Exemplo:

# Verificar todos os backups
backupally storage verify mysql-prod

# Verificação profunda
backupally storage verify mysql-prod --deep

# Verificar com histórico
backupally storage verify mysql-prod --check-history

# Verificar arquivo específico
backupally storage verify mysql-prod --file mysql-prod_20260212_020000.ally --deep

Saída:

🔍 Verificação de backups para 'mysql-prod'

   Modo: Verificação profunda (valida header .ally)
   Cruzando com histórico de execução

  ARQUIVO                                    EXISTE  LEGÍVEL  HEADER  HISTÓRICO  STATUS
  ─────────────────────────────────────────────────────────────────────────────────────
  mysql-prod_20260212_020000.ally           ✅      ✅       ✅      ✅ success  OK
  mysql-prod_20260211_020000.ally           ✅      ✅       ✅      ✅ success  OK
  mysql-prod_20260210_020000.ally           ✅      ✅       ❌      ⚠️ unknown  HEADER INVÁLIDO

  Resumo: 2 OK, 1 com problemas

storage registry

Cadastro de destinos em /etc/backupally/storages.d/ (desde 0.26.0). O destino é declarado uma vez, com id, e as tasks o referenciam em storage_refs em vez de repetir bucket e credencial em cada arquivo:

# /etc/backupally/pipeline/mysql-prod.toml
[task]
id = "mysql-prod"
storage_refs = ["b2-prod"]

O [[storage]] inline continua valendo, e os dois convivem na mesma task: inline é o formato de quem edita à mão; referência é o de quem automatiza e não quer carregar credencial de task em task.

backupally storage registry <SUBCOMANDO> [OPÇÕES]
Subcomando Descrição
add <ID> Cadastra um destino
remove <ID> Remove um destino do cadastro
list Lista os destinos cadastrados
show <ID> Mostra um destino (sem revelar segredo)
test <ID> Testa credenciais e acesso, em modo read-only

registry add

backupally storage registry add <ID> --driver <b2|s3|local> --bucket <BUCKET> [OPÇÕES]
Opção Descrição
--prefix <PREFIX> Prefixo/pasta dentro do bucket
--retention <POLÍTICA> Política de retenção, ex: "daily 7, weekly 4"
--description <TEXTO> Descrição livre
--set chave=valor Valor comum do driver, ex: --set region=us-west (repetível)
--secret chave=valor Credencial (repetível). Vai cifrada para o secret store e não é escrita no cadastro
--force Sobrescreve cadastro existente
backupally storage registry add b2-prod \
  --driver b2 --bucket backups-prod --prefix mysql \
  --retention "daily 7, weekly 4, monthly 6" \
  --secret key_id=000abc... \
  --secret application_key=K000...

O que acontece:

  • As credenciais vão para o secret store (cifrado com a chave do install) antes do arquivo ser escrito. Se o store falhar, nenhum cadastro fica apontando para secret inexistente
  • O arquivo /etc/backupally/storages.d/b2-prod.toml recebe só a referência ${secret:b2-prod_key_id} — o nome do secret é prefixado pelo id, para dois destinos com a mesma chave não se sobrescreverem
  • O arquivo é criado com permissão 0600
  • add recusa sobrescrever um id existente sem --force; id com / ou .. é rejeitado

registry show / list / test

backupally storage registry list
backupally storage registry show b2-prod
backupally storage registry test b2-prod

show mostra a referência ${secret:...} ou <literal oculto> no lugar de qualquer credencial — nunca o valor. test autentica no destino e lista, sem gravar nada.

Um arquivo inválido no cadastro não derruba os demais: aparece como erro no list e os outros destinos seguem utilizáveis.

registry remove

backupally storage registry remove b2-prod
backupally storage registry remove b2-prod --force

Sem --force, remove recusa remover um destino referenciado por alguma task e nomeia quais. Com --force, remove e avisa que essas tasks vão falhar no próximo run.

Os segredos não são apagados junto: podem pertencer a outro destino, e apagar segredo por engano é irreversível. Para removê-los, use `secrets`.

Referência desconhecida é erro

Uma task com storage_refs = ["nao-existe"] falha — no executor e no task validate --deep — com a lista do que está cadastrado. Seguir sem um destino que a task declarou significaria gravar em menos lugares do que o combinado, em silêncio.

Checksums por Driver

Cada driver de storage usa um tipo diferente de checksum:

Driver Tipo Descrição
Local SHA256 Calculado localmente
B2 SHA1 Retornado pelo Backblaze B2
S3 ETag MD5 (single-part) ou hash multipart

Tasks com Múltiplos Storages

Se uma task tem múltiplos storages configurados (replicação), use --index para selecionar:

# Storage primário (índice 0)
backupally storage list mysql-prod --index 0

# Storage secundário (índice 1)
backupally storage list mysql-prod --index 1

Exit Codes

Código Descrição
0 Sucesso
1 Erro (task não encontrada, storage inacessível, verificação falhou)

Verificação Profunda

Com --deep, o comando:

  1. Baixa os primeiros bytes do arquivo
  2. Valida o header (magic bytes)
  3. Verifica formatos suportados:
    • .ally (ALLY magic)
    • Zstd comprimido (0x28B52FFD)
    • Gzip comprimido (0x1F8B)

Cruzamento com Histórico

Com --check-history, o comando:

  1. Carrega logs de execução do diretório de logs
  2. Associa cada arquivo de backup com sua execução
  3. Exibe o status da execução (success, failed, unknown)

Útil para identificar backups que foram criados mas cuja execução falhou posteriormente.

Exemplos Práticos

Auditoria de Backups

# Verificar todos os backups com histórico
backupally storage verify mysql-prod --deep --check-history

# Exportar lista em JSON para análise
backupally storage list mysql-prod --checksums --format json > backups.json

Verificação Pré-Restore

# Antes de restaurar, verificar se backup está íntegro
backupally storage check mysql-prod mysql-prod_20260211_020000.ally

Monitoramento

# Script de monitoramento
if ! backupally storage verify mysql-prod --deep; then
    echo "ALERTA: Backups com problemas detectados"
    exit 1
fi
By Borlot.com.br on 04/09/2026