Arquivos de Task

Cada task de backup é definida em um arquivo TOML separado.

Localização

/etc/backupally/tasks.d/
├── mysql-prod.toml
├── website-backup.toml
└── mongodb-logs.toml

Estrutura Completa

[task]
id = "mysql-prod"
name = "MySQL Production Backup"
enabled = true
priority = 50
schedule = "0 2 * * *"
version = "1.0.0"

[target]
kind = "database"
driver = "mysql"
host = "localhost"
port = 3306
database = "*"
user = "${secret:mysql_user}"
password = "${secret:mysql_password}"
excludes = ["mysql", "performance_schema", "information_schema"]

[behavior]
process_mode = "individual"
on_error = "continue"
cleanup_on_error = true

[packer]
compressor = "zstd"
compression_level = 6
pack_size_mb = 100

[crypto]
key_mode = "default"

[network]
max_mbps = 50

[window]
start = "01:00"
end = "06:00"

[condition]
when = "loadavg() < 2.0 && diskfree('/backup') > 100GB"

[[storage]]
id = "s3-primary"
driver = "s3"
bucket = "backup-server"
region = "us-east-1"
path = "databases/mysql/${database_name}"
access_key = "${secret:aws_access_key}"
secret_key = "${secret:aws_secret_key}"
retention = "daily 7, weekly 4, monthly 6"

[[storage]]
id = "b2-secondary"
driver = "b2"
bucket = "backup-archive"
path = "mysql/${hostname}/${date}"
key_id = "${secret:b2_key_id}"
application_key = "${secret:b2_app_key}"
retention = "daily 7, weekly 4"

[notification]
enabled = true
only_on_error = false

[notification.webhook]
url = "https://hooks.slack.com/services/xxx"
method = "POST"

Seções

[task]

Metadados da task.

Campo Tipo Obrigatório Descrição
id string Sim Identificador único
name string Não Nome descritivo
enabled bool Não Habilitada (padrão: true)
priority int Não Prioridade 0-100 (padrão: 50)
schedule string Não Expressão cron
timezone string Não Fuso do schedule, nome IANA (padrão: UTC)
storage_refs array Não Ids de destinos do cadastro de storages. Convive com [[storage]] inline. Id desconhecido é erro
version string Não Versão da configuração

timezone ausente significa UTC, não o fuso do servidor. Num servidor em -03, schedule = "30 3 * * *" sem timezone dispara 00:30 no horário local. Ver Agendamento.

[target]

Origem dos dados.

Campos Comuns

Campo Tipo Descrição
kind string Tipo: database, fileset, stdout, custom

Database

Campo Tipo Descrição
driver string mysql, postgres, mongodb, clickhouse, sqlite
host string Hostname, IP, ou docker:<container>[/<rede>] (ver abaixo)
port int Porta
database string Nome do banco (* para todos)
user string Usuário
password string Senha (use ${secret:name})
excludes array Bancos a excluir
ssh tabela Túnel por bastion — ver Banco atrás de Bastion
Banco em container Docker

Em vez de fixar o IP do container — que muda quando ele é recriado — declare o container:

host = "docker:mysql"           # container em uma única rede
host = "docker:mysql/interna"   # container em várias redes: diga qual usar

O IP é resolvido a cada execução. Se o container estiver em duas ou mais redes e a rede não for declarada, o backup falha com uma mensagem listando as redes disponíveis — em vez de escolher uma no escuro:

Container 'mysql' esta em 2 redes (interna=192.0.2.10, publica=192.0.2.20)
— impossivel escolher automaticamente. Declare qual usar:
`host = "docker:mysql/<rede>"` ou `container_network = "<rede>"` no config da task.

A rede também pode ser declarada no campo container_network, equivalente.

Fileset

Campo Tipo Descrição
includes array Caminhos a incluir
excludes array Padrões a excluir

[behavior]

Comportamento de execução.

Campo Tipo Padrão Descrição
process_mode string individual individual, batch
on_error string continue stop, continue, skip
cleanup_on_error bool true Limpar temporários em erro

[collector]

Seção sem efeito: chunk_algo, chunk_avg_mb e incremental são lidas e ignoradas — não existe chunking nem backup incremental. Pode ser omitida. Ver Como o Backup é Armazenado.

[packer]

Compressão e empacotamento.

Campo Tipo Padrão Descrição
compressor string zstd zstd ou none. lz4 é aceito mas grava gzip; qualquer outro valor cai em zstd
compression_level int - Sem efeito — zstd roda sempre no nível 3
pack_size_mb int - Sem efeito — cada run é um arquivo só

[crypto]

Criptografia da task.

Campo Tipo Padrão Descrição
key_mode string default default, ephemeral, none
key_ref string - Chave específica

[network]

Rede da task.

Campo Tipo Descrição
max_mbps int Limite de banda (Mbps)

[window]

Janela de execução.

Campo Tipo Descrição
start string Horário inicial (HH:MM)
end string Horário final (HH:MM)

[condition]

Condições de execução.

Campo Tipo Descrição
when string Expressão de condição

Funções disponíveis:

Função Descrição
loadavg() Média de carga do sistema
diskfree('path') Espaço livre em disco
time() Hora atual
dayofweek() Dia da semana (1-7)

[[storage]]

Destinos de backup (pode haver múltiplos).

Campo Tipo Descrição
id string Nome do destino — aparece nos logs e no diagnóstico. Com mais de um destino, é o que diz qual deles falhou. Ausente: gerado a partir da task

Ver Configuração de Storage para detalhes dos demais campos.

Alternativa ao bloco inline: storage_refs = ["b2-prod"] em [task], apontando para um destino cadastrado com backupally storage registry add. A task não carrega credencial nenhuma; ela fica no cadastro, e o cadastro guarda só a referência ao secret store. Ver storage registry.

[notification]

Notificações.

Campo Tipo Descrição
enabled bool Habilitar notificações
only_on_error bool Notificar apenas em erro

Exemplos por Tipo

MySQL

[task]
id = "mysql-prod"
schedule = "0 2 * * *"

[target]
kind = "database"
driver = "mysql"
host = "localhost"
database = "*"
user = "${secret:mysql_user}"
password = "${secret:mysql_password}"
excludes = ["mysql", "performance_schema", "information_schema"]

[packer]
compressor = "zstd"

[[storage]]
driver = "s3"
bucket = "backups"
retention = "daily 7, weekly 4"

PostgreSQL

[task]
id = "postgres-prod"
schedule = "0 3 * * *"

[target]
kind = "database"
driver = "postgres"
host = "localhost"
database = "myapp"
user = "${secret:pg_user}"
password = "${secret:pg_password}"
dump_globals = true

[[storage]]
driver = "local"
path = "/backups/postgres"
retention = "daily 7"

Fileset

[task]
id = "website-files"
schedule = "0 4 * * *"

[target]
kind = "fileset"
includes = ["/var/www/html", "/etc/nginx"]
excludes = ["**/.git", "**/node_modules", "**/*.log"]

[packer]
compressor = "zstd"

[[storage]]
driver = "b2"
bucket = "website-backups"
retention = "daily 7, weekly 4"

MongoDB

[task]
id = "mongodb-prod"
schedule = "0 2 * * *"

[target]
kind = "database"
driver = "mongodb"
uri = "mongodb://localhost:27017"
database = "*"
oplog = true
gzip = true

[[storage]]
driver = "s3"
bucket = "mongo-backups"
retention = "daily 7"

ClickHouse

[task]
id = "clickhouse"
schedule = "45 2 * * *"

[target]
kind = "database"
driver = "clickhouse"
host = "docker:clickhouse/clickhouse"
port = 8123               # interface HTTP
database = "*"            # system e information_schema ficam de fora
user = "${secret:clickhouse_user}"
password = "${secret:clickhouse_password}"

[[storage]]
driver = "b2"
bucket = "meus-backups"
path = "clickhouse/${database_name}"
retention = "daily 14, weekly 8"

O dump é lógico: um tar com <banco>/<tabela>.sql (o SHOW CREATE TABLE) e <banco>/<tabela>.json (os dados em JSONEachRow). Serve para restaurar em qualquer versão do ClickHouse, mas para tabelas grandes é lento dos dois lados. O usuário precisa de SHOW e SELECT e de acesso pela rede — o default do container oficial só aceita localhost:

CREATE USER backupally IDENTIFIED WITH sha256_password BY '...' HOST ANY;
GRANT SHOW, SELECT ON *.* TO backupally;

Para um backup nativo (partes binárias, BACKUP DATABASE ... TO Disk), use uma task custom com um script que roda o BACKUP no container e escreve o resultado em tar no stdout:

[target]
kind = "custom"
command = "/etc/backupally/scripts/clickhouse-native.sh"

A saída do comando vira o conteúdo do backup. O script exige um disco backups declarado no config.d do servidor (<backups><allowed_disk>backups</allowed_disk></backups>).

Hot Reload

Mudanças em arquivos de task são detectadas automaticamente a cada 5 segundos. Não é necessário reiniciar o scheduler.

By Borlot.com.br on 23/08/2026