De .restic.sh para .rustic.sh: adotando perfis nativos no rustic e corrigindo armadilhas silenciosas
Como aposentei de vez o legado ~/.restic.sh em favor do ~/.rustic.sh com perfis TOML nativos, herança de configuração, um único comando de backup por repositório, correção de exclusões silenciosas com as-path e um manual duplo de restauração para rustic e restic.
No post anterior, A minha nova rotina de backup com o rustic, eu contei como troquei o motor da minha rotina — que originalmente havia nascido em A minha rotina de backup com o restic — passando do restic para o rustic escrito em Rust.
Só tinha um detalhe que continuava me incomodando toda vez que eu abria o terminal ou o crontab -e: eu havia trocado o motor, mas deixado a lataria inteira do restic no lugar. O script continuava morando em ~/.restic.sh, disparava quatro comandos backup em sequência passando dezenas de flags repetidas na linha de comando e mantinha uma lista de exclusões herdada no piloto automático.
Hoje eu resolvi pagar essa dívida técnica. Migrei oficialmente de ~/.restic.sh para ~/.rustic.sh, adotei os perfis de configuração nativos em TOML do rustic — e, no caminho, descobri bugs silenciosos que poderiam ter custado espaço em nuvem ou dor de cabeça na hora de uma restauração real.
Por que sair de flags repetidas para perfis nativos?
Quando apenas substituímos restic por rustic dentro de um shell script antigo, perdemos grande parte da ergonomia e da eficiência que o rustic entrega nativamente por meio dos seus arquivos de configuração e perfis.
Os principais motivos para a mudança foram:
- Fim do nome legado e confusão no cron: manter um script do
rusticchamado~/.restic.shsó fazia sentido na fase de transição. Atualizar o arquivo e apontar ocrontab -epara~/.rustic.shdeixa a rotina coerente. - Um arquivo base com herança (
use-profiles): em vez de repetir senha, política de retenção (keep-daily,keep-weekly,keep-monthly,keep-tags,prune), exclusões e origens em arrays de Bash para cada repositório, tudo fica centralizado em/etc/rustic/base.toml. Os perfisnuvem.tomlelocal.tomlapenas herdam esse perfil base e definem o destino específico (além das opções deretryetimeoutdorcloneno perfil remoto). - Visibilidade para
roote para o usuário: como o backup roda viasudonocrontab, colocar os perfis em/etc/rustic/com permissão644garante que tanto os jobs agendados quanto consultas manuais usem exatamente as mesmas definições. - Quatro backups viram um só por repositório: usando blocos
[[backup.snapshots]]no TOML, um únicosudo rustic -P nuvem backuplê os índices do repositório remoto uma única vez, sobe o transporte via rclone uma única vez e executa os quatro snapshots configurados em sequência — reduzindo overhead, tempo de leitura e risco de inconsistência entre etapas. - Comandos do dia a dia muito mais curtos: sem precisar digitar
--repoou--password-fileno terminal. Comrustic.tomlapontando paralocal, um simplessudo rustic snapshotsjá lista o NAS/repositório local, esudo rustic -P nuvem snapshotsconsulta a nuvem.
Ganhos técnicos e armadilhas encontradas no caminho
Reescrever o script obrigou a testar cada detalhe em repositórios temporários e inspecionar snapshots antigos. Foi aí que apareceram quatro achados valiosos.
1. O bug silencioso do !./var/tmp/* e a elegância do as-path = "/"
No script antigo, antes de fazer o backup do sistema raiz via snapshot Btrfs, eu rodava um cd /.snap_root seguido de rustic backup . com exclusões como --glob "!./tmp/*" e --glob "!./var/tmp/*".
Ao inspecionar um snapshot antigo com rustic cat tree, veio o susto: o conteúdo de /var/tmp — incluindo arquivos temporários de serviços e até artefatos temporários de exportação do Docker — estava sendo copiado. O prefixo ./ na regra --glob simplesmente não batia como eu imaginava dentro do rustic.
Consultando a documentação oficial de exclusão de arquivos do rustic e validando em repositório de teste, a solução nativa ficou muito mais limpa:
- Apontar diretamente
sources = ["/.snap_root"]; - Definir
as-path = "/"para gravar o snapshot já normalizado como/, e não com o caminho temporário/.snap_root; - Ancorar os globs explicitamente no diretório de origem:
"!/.snap_root/tmp/*","!/.snap_root/var/tmp/*","!/.snap_root/timeshift".
Na prática, os diretórios /tmp e /var/tmp vazios continuam sendo recriados na restauração, o lixo temporário fica de fora e, para todos os snapshots novos, não é mais preciso mover arquivos de /.snap_root/* para / depois de restaurar.
2. Herança de globs em [[backup.snapshots]] não faz append
Esse detalhe quase passou batido, mas está documentado no exemplo completo oficial (config/full.toml): quando um bloco [[backup.snapshots]] omite globs ou exclude-if-present, ele herda as configurações da seção [backup]. Porém, quando um bloco define o seu próprio globs = [...], ele substitui o globs global em vez de somar.
É por isso que, nos perfis de dados-pessoais e pasta-sync, eu mantive "!**/.cache" e exclude-if-present = ["CACHEDIR.TAG", ".nobackup"] explicitamente dentro dos respectivos blocos. Testar com rustic show-config confirmou o comportamento antes mesmo do primeiro backup real.
3. Higiene de execução, trap EXIT e notificações honestas
Se um backup for interrompido no meio do caminho, um snapshot read-only /.snap_root ou um diretório /var/tmp/docker_bkp_temp esquecido pode ficar para trás (e até vazar para execuções futuras). Para blindar o fluxo:
- Adicionei
trap limpar_temporarios EXIT, garantindo remoção automática ao sair — inclusive em caso de erro ou validação; - Também chamo
limpar_temporarioslogo antes de criar o snapshot novo; - Troquei redirecionamentos com
sudo(sudo cmd > arquivo, que falham fora de um shell root) por| sudo tee ... > /dev/nullemapfile -tpara capturar IDs de containers e imagens do Docker com segurança; - Passei a controlar um contador
ERROSpara que o alerta de sucesso via ntfy só dispare quandoERROS -eq 0— evitando aquele falso positivo clássico de "Backup Concluído" após alguma etapa falhar silenciosamente; - Criei três flags úteis de manutenção:
-n | --dry-run,--apenas-perfis(para regerar/etc/rustic/*.tomlsem rodar backup) e--validar(que executa o fluxo completo sem upload nemdocker save, conferindoshow-config,--dry-rune limpeza automática viatrap).
Dica extra para quem quiser ir além: o
rustictambém possui hooks nativos no arquivo de configuração (hooks.backup-before,hooks.backup-after), mas eu preferi manter a criação do snapshot Btrfs e o dump de metadados/imagens do Docker no script em Bash para compartilhar um único ciclo de preparo e limpeza entre os dois repositórios (nuvemelocal), além de controlar facilmente modos como--validare as notificações nontfy.
4. A armadilha do comando restore entre rustic e restic (e subvolumes Btrfs)
Uma das maiores vantagens do rustic é manter compatibilidade com o formato de repositório do restic. Em uma pane total, você pode restaurar seus dados usando tanto rustic quanto restic.
Mas há uma pegadinha perigosa no manual antigo de desastres que estava comentado no meu script: quando eu havia substituído a palavra restic por rustic no comentário, sobrou um comando inválido (rustic restore latest --tag ... --target ...). Na prática, as sintaxes são diferentes:
- No rustic (
restore): usa-se--filter-tags "<tag>"e o destino é posicional (rustic restore latest --filter-tags "sistema-raiz" /mnt/@). - No restic (
restore): usa-se--tag "<tag>"e--target <destino>(restic restore latest --tag "sistema-raiz" --target /mnt/@).
Além disso, como uso Manjaro com layout padrão de subvolumes Btrfs (@ para / e @home para /home), recriar o subvolume @ em /mnt/@ exige lembrar de montar também subvol=@home em /mnt/@/home caso vá restaurar os dados pessoais pelo Live USB — senão tudo acaba gravado dentro de @. Documentei as duas opções passo a passo diretamente no final do script.
Como os perfis ficaram estruturados em /etc/rustic
O próprio script gera e mantém sincronizados quatro arquivos em /etc/rustic/ (aqui com caminhos, remotos, tópicos e host anonimizados):
# /etc/rustic/base.toml [global] log-level = "info" progress-interval = "1min" group-by = "host,label,paths" [repository] password-file = "/caminho/seguro/.senha.txt" [backup] exclude-if-present = ["CACHEDIR.TAG", ".nobackup"] globs = ["!**/.cache"] [[backup.snapshots]] name = "sistema-raiz" sources = ["/.snap_root"] as-path = "/" tags = ["sistema-raiz"] one-file-system = true globs = [ "!/.snap_root/tmp/*", "!/.snap_root/var/tmp/*", "!/.snap_root/timeshift", ] [[backup.snapshots]] name = "pasta-sync" sources = ["/home/usuario/Sync"] tags = ["pasta-sync"] exclude-if-present = ["CACHEDIR.TAG", ".nobackup"] globs = ["!**/.cache"] [[backup.snapshots]] name = "dados-pessoais" sources = ["/home", "/dados"] tags = ["dados-pessoais"] exclude-if-present = ["CACHEDIR.TAG", ".nobackup"] globs = [ "!/dados/backup", "!**/.cache", ] [[backup.snapshots]] name = "docker-data" sources = ["/var/lib/docker/volumes", "/var/tmp/docker_bkp_temp"] tags = ["docker-data"] [forget] keep-daily = 7 keep-weekly = 2 keep-monthly = 1 keep-tags = ["keep"] prune = true
E os perfis específicos ficam enxutos assim:
# /etc/rustic/nuvem.toml [global] use-profiles = ["base"] [repository] repository = "rclone:meu_remoto:backup/desktop" [repository.options] retry = "default" timeout = "10min"
# /etc/rustic/local.toml [global] use-profiles = ["base"] [repository] repository = "/mnt/nas/backup"
# /etc/rustic/rustic.toml [global] use-profiles = ["local"]
O script completo (~/.rustic.sh) e o crontab -e
Abaixo está o script completo que passou a rodar na máquina (também mantido com uma cópia na pasta de sincronização, já anonimizado para publicação sem expor caminhos reais, remotos ou segredos):
#!/usr/bin/env bash # ============================================================================== # SCRIPT DE BACKUP COMPLETO (SISTEMA + DADOS + DOCKER) - RUSTIC (PERFIS) & BTRFS # ============================================================================== set -u LOG_FILE="/home/usuario/rustic_backup_completo.log" PASSWORD_FILE="/caminho/seguro/.senha.txt" SYNC_DIR="/home/usuario/Sync" NTFY_URL="https://ntfy.exemplo.work/meu-topico-secreto" DOCKER_BKP_DIR="/var/tmp/docker_bkp_temp" RUSTIC_CONFIG_DIR="/etc/rustic" REPO_NUVEM="rclone:meu_remoto:backup/desktop" REPO_LOCAL="/mnt/nas/backup" DRY_RUN=0 APENAS_PERFIS=0 VALIDAR=0 ERROS=0 for arg in "$@"; do case "$arg" in -n|--dry-run) DRY_RUN=1 ;; --apenas-perfis) APENAS_PERFIS=1 ;; --validar) VALIDAR=1 DRY_RUN=1 ;; *) echo "Uso: $0 [-n|--dry-run] [--apenas-perfis] [--validar]" >&2 exit 2 ;; esac done # Redireciona stdout e stderr para o log com data e hora em execuções normais if [ "$APENAS_PERFIS" -eq 0 ] && [ "$VALIDAR" -eq 0 ]; then exec > >(awk -v logfile="$LOG_FILE" '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0 >> logfile; close(logfile) }') 2>&1 fi # Envia alertas de erro para o ntfy (Prioridade: default) notificar_erro() { local mensagem="$1" ERROS=$((ERROS + 1)) echo "[ALERTA] $mensagem" if [ "$DRY_RUN" -eq 1 ]; then echo "[DRY-RUN] Notificação ntfy suprimida." return 0 fi curl -s -o /dev/null \ -H "Title: Falha no Backup (Desktop)" \ -H "Tags: rotating_light,skull" \ -H "Priority: default" \ -d "$mensagem" \ "$NTFY_URL" || true } # Notifica sucesso apenas quando nenhuma etapa falhou (Prioridade: min) notificar_sucesso() { if [ "$DRY_RUN" -eq 1 ]; then echo "[DRY-RUN] Notificação de sucesso ntfy suprimida." return 0 fi curl -s -o /dev/null \ -H "Title: Backup Concluído (Desktop)" \ -H "Tags: white_check_mark,floppy_disk" \ -H "Priority: min" \ -d "Rotina de backup e verificação finalizada sem interrupções." \ "$NTFY_URL" || true } # Remove snapshot Btrfs e diretório temporário do Docker ao sair ou antes de iniciar limpar_temporarios() { cd / || return 0 if sudo btrfs subvolume show /.snap_root >/dev/null 2>&1; then sudo btrfs subvolume delete /.snap_root >/dev/null 2>&1 || true fi if [ -d "$DOCKER_BKP_DIR" ]; then sudo rm -rf "$DOCKER_BKP_DIR" || true fi } # Gera/atualiza os perfis TOML nativos em /etc/rustic (visível ao sudo e ao usuário) garantir_perfis_rustic() { sudo mkdir -p "$RUSTIC_CONFIG_DIR" sudo chmod 755 "$RUSTIC_CONFIG_DIR" sudo tee "$RUSTIC_CONFIG_DIR/base.toml" > /dev/null <<EOF # Perfil base compartilhado entre todos os repositórios (herdado via use-profiles) [global] log-level = "info" progress-interval = "1min" group-by = "host,label,paths" [repository] password-file = "$PASSWORD_FILE" [backup] exclude-if-present = ["CACHEDIR.TAG", ".nobackup"] globs = ["!**/.cache"] [[backup.snapshots]] name = "sistema-raiz" sources = ["/.snap_root"] as-path = "/" tags = ["sistema-raiz"] one-file-system = true globs = [ "!/.snap_root/tmp/*", "!/.snap_root/var/tmp/*", "!/.snap_root/timeshift", ] [[backup.snapshots]] name = "pasta-sync" sources = ["$SYNC_DIR"] tags = ["pasta-sync"] exclude-if-present = ["CACHEDIR.TAG", ".nobackup"] globs = ["!**/.cache"] [[backup.snapshots]] name = "dados-pessoais" sources = ["/home", "/dados"] tags = ["dados-pessoais"] exclude-if-present = ["CACHEDIR.TAG", ".nobackup"] globs = [ "!/dados/backup", "!**/.cache", ] [[backup.snapshots]] name = "docker-data" sources = ["/var/lib/docker/volumes", "$DOCKER_BKP_DIR"] tags = ["docker-data"] [forget] keep-daily = 7 keep-weekly = 2 keep-monthly = 1 keep-tags = ["keep"] prune = true EOF sudo tee "$RUSTIC_CONFIG_DIR/nuvem.toml" > /dev/null <<EOF # Perfil do repositório remoto na nuvem (via rclone) [global] use-profiles = ["base"] [repository] repository = "$REPO_NUVEM" [repository.options] retry = "default" timeout = "10min" EOF sudo tee "$RUSTIC_CONFIG_DIR/local.toml" > /dev/null <<EOF # Perfil do repositório local / NAS ($REPO_LOCAL) [global] use-profiles = ["base"] [repository] repository = "$REPO_LOCAL" EOF sudo tee "$RUSTIC_CONFIG_DIR/rustic.toml" > /dev/null <<'EOF' # Perfil padrão quando -P não é informado na linha de comando [global] use-profiles = ["local"] EOF sudo chmod 644 \ "$RUSTIC_CONFIG_DIR/base.toml" \ "$RUSTIC_CONFIG_DIR/nuvem.toml" \ "$RUSTIC_CONFIG_DIR/local.toml" \ "$RUSTIC_CONFIG_DIR/rustic.toml" } garantir_perfis_rustic if [ "$APENAS_PERFIS" -eq 1 ]; then echo "[INFO] Perfis do rustic atualizados em $RUSTIC_CONFIG_DIR." exit 0 fi trap limpar_temporarios EXIT echo "=== Iniciando rotina de backup COMPLETA (Perfis Nativos) ===" # 1. Limpar eventuais resíduos anteriores e congelar o sistema raiz (Snapshot Btrfs Read-Only) limpar_temporarios echo "[INFO] Criando snapshot do sistema raiz (/)..." sudo btrfs subvolume snapshot -r / /.snap_root || notificar_erro "Falha ao criar o snapshot do Btrfs raiz." # 2. Preparação do Backup do Docker (Imagens e Configurações) echo "[INFO] Coletando metadados e imagens do Docker..." sudo mkdir -p "$DOCKER_BKP_DIR" mapfile -t CONTAINER_IDS < <(sudo docker ps -aq) if [ "${#CONTAINER_IDS[@]}" -gt 0 ]; then sudo docker ps -a | sudo tee "$DOCKER_BKP_DIR/containers_list.txt" > /dev/null sudo docker inspect "${CONTAINER_IDS[@]}" 2>/dev/null | sudo tee "$DOCKER_BKP_DIR/containers_inspect.json" > /dev/null || true else echo "Nenhum container encontrado." | sudo tee "$DOCKER_BKP_DIR/containers_list.txt" > /dev/null fi if [ "$VALIDAR" -eq 1 ]; then echo "[VALIDAÇÃO] Exportação completa de docker_images.tar pulada no modo --validar." echo "Validação em dry-run." | sudo tee "$DOCKER_BKP_DIR/docker_images_validacao.txt" > /dev/null else echo "[INFO] Exportando imagens do Docker para arquivo .tar..." mapfile -t IMAGE_IDS < <(sudo docker images -q) if [ "${#IMAGE_IDS[@]}" -gt 0 ]; then sudo docker save "${IMAGE_IDS[@]}" -o "$DOCKER_BKP_DIR/docker_images.tar" 2>/dev/null || notificar_erro "Falha ao exportar imagens do Docker." else echo "Nenhuma imagem encontrada." | sudo tee "$DOCKER_BKP_DIR/docker_images.txt" > /dev/null fi fi # Modo rápido de validação do fluxo completo (Btrfs + Docker + Perfis + trap) if [ "$VALIDAR" -eq 1 ]; then echo "[VALIDAÇÃO] Verificando carregamento dos perfis local e nuvem..." sudo rustic -P local show-config > /dev/null sudo rustic -P nuvem show-config > /dev/null echo "[VALIDAÇÃO] Testando backup --dry-run (pasta-sync) e forget --dry-run no perfil local..." sudo rustic -P local --dry-run backup --name pasta-sync sudo rustic -P local --dry-run forget echo "=== Validação concluída com sucesso (limpeza será executada pelo trap EXIT) ===" exit 0 fi # 3. Definição dos Perfis de Repositório (/etc/rustic/<perfil>.toml) PERFIS=( "nuvem" "local" ) RUSTIC_ARGS=() if [ "$DRY_RUN" -eq 1 ]; then RUSTIC_ARGS+=(--dry-run) fi # 4. Execução para cada perfil (um único comando backup cria todos os [[backup.snapshots]]) for PERFIL in "${PERFIS[@]}"; do echo "--> Processando perfil: $PERFIL" # 4.1 Backup unificado (sistema-raiz, pasta-sync, dados-pessoais e docker-data) echo "[INFO] Executando backup de todos os snapshots do perfil $PERFIL..." sudo rustic -P "$PERFIL" "${RUSTIC_ARGS[@]}" backup \ || notificar_erro "Erro ao realizar backup no perfil: $PERFIL" # 4.2 Rotação de retenção e poda (prune = true já está configurado no [forget] de base.toml) echo "[INFO] Aplicando política de retenção e prune no perfil $PERFIL..." sudo rustic -P "$PERFIL" "${RUSTIC_ARGS[@]}" forget \ || notificar_erro "Erro ao realizar a limpeza (forget/prune) no perfil: $PERFIL" # 4.3 Verificação de integridade do repositório if [ "$DRY_RUN" -eq 1 ]; then echo "[DRY-RUN] Verificação pesada (check) ignorada para o perfil: $PERFIL" else echo "[INFO] Verificando integridade do repositório ($PERFIL)..." sudo rustic -P "$PERFIL" check \ || notificar_erro "ATENÇÃO: Falha de INTEGRIDADE detectada no perfil: $PERFIL" fi echo "--> Finalizado: $PERFIL" echo "----------------------------------------" done # 5. Limpeza Final (Btrfs e Docker) echo "[INFO] Removendo snapshot temporário do Btrfs e dados temporários do Docker..." limpar_temporarios if [ "$ERROS" -eq 0 ]; then echo "=== Backup concluído com sucesso ===" notificar_sucesso else echo "=== Backup finalizado com $ERROS falha(s) ===" exit 1 fi # ============================================================================== # MANUAL DE RESTAURAÇÃO EM CASO DE CATÁSTROFE (BARE METAL RESTORE) # ============================================================================== # O formato de repositório do rustic é 100% compatível com o do restic. # Portanto, em uma emergência você pode restaurar usando QUALQUER UMA das duas # ferramentas (rustic OU restic), conforme a disponibilidade no Live USB. # # ATENÇÃO À SINTAXE: # - rustic restore usa `--filter-tags "<tag>"` e destino POSICIONAL (sem `--target`). # - restic restore usa `--tag "<tag>"` e `--target <destino>`. # # 1. DÊ BOOT POR UM PENDRIVE (Live USB do Manjaro). # # 2. MONTE A PARTIÇÃO BTRFS PRINCIPAL (Substitua sdXN pela partição raiz, ex: nvme0n1p2): # sudo mount /dev/sdXN /mnt # # 3. APAGUE O SUBVOLUME QUEBRADO E CRIE UM NOVO LIMPO: # sudo btrfs subvolume delete /mnt/@ # sudo btrfs subvolume create /mnt/@ # (Se também precisar restaurar o @home do zero, recrie /mnt/@home e monte em /mnt/@/home: # sudo mkdir -p /mnt/@/home && sudo mount -o subvol=@home /dev/sdXN /mnt/@/home) # # ------------------------------------------------------------------------------ # OPÇÃO A: RESTAURANDO COM O RUSTIC # ------------------------------------------------------------------------------ # 4A. INSTALE O RUSTIC NO LIVE USB: # sudo pacman -Sy rustic # # Ou via binário estático pré-compilado (útil se o pacote estiver indisponível): # curl -L https://github.com/rustic-rs/rustic/releases/latest/download/rustic-x86_64-unknown-linux-musl.tar.gz \ # | tar -xz && sudo mv rustic /usr/local/bin/ # # 5A. CONFIGURE AS CREDENCIAIS E RESTAURE OS SNAPSHOTS COM O RUSTIC: # export RUSTIC_PASSWORD="sua_senha_aqui" # export RUSTIC_REPOSITORY="rclone:meu_remoto:backup/desktop" # ou /caminho/do/repositorio/local # # Sistema operacional (raiz): # sudo -E rustic restore latest --filter-tags "sistema-raiz" /mnt/@ # # Dados pessoais (/home e /dados) e pasta Sync: # sudo -E rustic restore latest --filter-tags "dados-pessoais" /mnt/@ # sudo -E rustic restore latest --filter-tags "pasta-sync" /mnt/@ # # (Dica: se já estiver com o sistema rodando e os perfis em /etc/rustic/, basta: # # sudo rustic -P local restore latest --filter-tags "pasta-sync" /) # # ------------------------------------------------------------------------------ # OPÇÃO B: RESTAURANDO COM O RESTIC # ------------------------------------------------------------------------------ # 4B. INSTALE O RESTIC NO LIVE USB (se não vier pré-instalado): # sudo pacman -Sy restic # # 5B. CONFIGURE AS CREDENCIAIS E RESTAURE OS SNAPSHOTS COM O RESTIC: # export RESTIC_PASSWORD="sua_senha_aqui" # export RESTIC_REPOSITORY="rclone:meu_remoto:backup/desktop" # ou /caminho/do/repositorio/local # # Sistema operacional (raiz): # sudo -E restic restore latest --tag "sistema-raiz" --target /mnt/@ # # Dados pessoais (/home e /dados) e pasta Sync: # sudo -E restic restore latest --tag "dados-pessoais" --target /mnt/@ # sudo -E restic restore latest --tag "pasta-sync" --target /mnt/@ # # ------------------------------------------------------------------------------ # 6. OBSERVAÇÃO SOBRE O CAMINHO DOS ARQUIVOS RESTAURADOS: # - Snapshots novos (gerados com `as-path = "/"` nos perfis do rustic) já # colocam /etc, /usr, /var, etc. diretamente em /mnt/@/. Não é preciso mover nada! # - Apenas se você restaurar um snapshot ANTIGO (anterior à migração, que # salvava o prefixo /.snap_root), ajuste os caminhos com: # sudo mv /mnt/@/.snap_root/* /mnt/@/ # sudo mv /mnt/@/.snap_root/.* /mnt/@/ 2>/dev/null || true # sudo rmdir /mnt/@/.snap_root # # 7. DESMONTE SUBVOLUMES TEMPORÁRIOS E REINSTALE O BOOTLOADER (GRUB): # sudo umount -R /mnt # O Manjaro possui o utilitário nativo `manjaro-chroot`: # sudo manjaro-chroot -a # (Selecione a instalação restaurada). Dentro do chroot, rode: # grub-install /dev/sdX # Substitua sdX pelo disco (ex: /dev/nvme0n1), SEM número de partição # update-grub # exit # # 8. RESTAURAÇÃO DO DOCKER (se necessário reconstruir separadamente após subir o sistema): # - Usando rustic: # sudo -E rustic restore latest --filter-tags "docker-data" / # # (ou com perfil: sudo rustic -P local restore latest --filter-tags "docker-data" /) # - Ou usando restic: # sudo -E restic restore latest --tag "docker-data" --target / # - Isso devolverá os volumes para /var/lib/docker/volumes e os arquivos de # metadados/imagens para /var/tmp/docker_bkp_temp/. # - Recarregue as imagens Docker: # sudo docker load -i /var/tmp/docker_bkp_temp/docker_images.tar # - Recrie os containers usando seus docker-compose.yml originais ou consultando # /var/tmp/docker_bkp_temp/containers_list.txt e containers_inspect.json. # - Limpe o diretório temporário ao terminar: # sudo rm -rf /var/tmp/docker_bkp_temp # # 9. REINICIE E VALIDE: # reboot # ==============================================================================
No crontab -e, bastou atualizar o caminho do script antigo para o novo:
0 3 * * * sudo /home/usuario/.rustic.sh >/dev/null 2>&1
Validando na prática: comandos e resultados observados
Depois de validar a sintaxe e boas práticas com bash -n ~/.rustic.sh && shellcheck ~/.rustic.sh (sem avisos), a verificação em ambiente real confirmou os ganhos dos perfis:
# 1. Gerar apenas os arquivos TOML em /etc/rustic/ ~/.rustic.sh --apenas-perfis # 2. Inspecionar a configuração mesclada (base + perfil específico) sudo rustic -P local show-config sudo rustic -P nuvem show-config # 3. Consultar snapshots sem passar --repo nem --password-file sudo rustic snapshots # usa /etc/rustic/rustic.toml -> local (117 snapshots encontrados) sudo rustic -P nuvem snapshots # consulta o repositório remoto (108 snapshots encontrados) # 4. Rodar apenas um snapshot específico do perfil (ex: pasta-sync) sudo rustic -P local backup --name pasta-sync
Nesse teste incremental com --name pasta-sync, o rustic processou 756 arquivos (1,3 GiB) em um único disparo direcionado, identificou 1 arquivo novo e 1 alterado (justamente a cópia atualizada do próprio script na pasta sincronizada), adicionou 59,3 MiB compactados (de 79,2 MiB brutos) ao repositório local e salvou o snapshot 26450f62 sem precisar rodar os outros três alvos. Em seguida, sudo rustic -P local forget --dry-run e sudo rustic -P local check confirmaram a política de retenção e a integridade do repositório local.
E para testar toda a engrenagem antes das 3h da manhã — criação do snapshot Btrfs, coleta de metadados do Docker, leitura dos perfis local e nuvem, backup --dry-run, forget --dry-run e remoção limpa dos arquivos temporários via trap EXIT — basta chamar:
~/.rustic.sh --validar
Agora o nome combina com a ferramenta, a configuração mora onde deve morar, as exclusões realmente excluem o que devem excluir e, se o pior acontecer algum dia, tanto o rustic quanto o bom e velho restic estão documentados com os comandos certos na ponta dos dedos.
Fontes e referências
- Post anterior sobre a adoção do rustic: A minha nova rotina de backup com o rustic (Quadrante do Giuliano)
- Post original da série com restic: A minha rotina de backup com o restic (Quadrante do Giuliano)
- Documentação oficial de arquivos de configuração e perfis do rustic: Configuration file — rustic docs
- Guia oficial de perfis no repositório do rustic: config/README.md — rustic-rs/rustic (GitHub)
- Referência completa de opções TOML do rustic: config/full.toml — rustic-rs/rustic (GitHub)
- Documentação de exclusões e globs do rustic: Excluding files — rustic docs
- Documentação de hooks no rustic: Hooks — rustic docs
- Documentação oficial de restauração do rustic: Restore — rustic docs
- Documentação oficial de restauração do restic: Restoring from backup — restic docs
- Especificação e documento de design do formato restic: References & Design Document — restic docs
- Guia de Btrfs na ArchWiki: Btrfs — ArchWiki
- Documentação oficial do Btrfs: BTRFS documentation
- Distribuição utilizada nos exemplos de recuperação: Manjaro Linux
- Plataforma de containers incluída no backup: Docker
- Ferramenta de sincronização em nuvem usada no repositório remoto: Rclone
- Serviço de notificações push usado para alertas de erro e sucesso: ntfy