quadrante — onde o hiperfoco reina
Quadrante

Utilitários

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.

Giuliano Cardoso · · 19 min de leitura

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:

  1. Fim do nome legado e confusão no cron: manter um script do rustic chamado ~/.restic.sh só fazia sentido na fase de transição. Atualizar o arquivo e apontar o crontab -e para ~/.rustic.sh deixa a rotina coerente.
  2. 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 perfis nuvem.toml e local.toml apenas herdam esse perfil base e definem o destino específico (além das opções de retry e timeout do rclone no perfil remoto).
  3. Visibilidade para root e para o usuário: como o backup roda via sudo no crontab, colocar os perfis em /etc/rustic/ com permissão 644 garante que tanto os jobs agendados quanto consultas manuais usem exatamente as mesmas definições.
  4. Quatro backups viram um só por repositório: usando blocos [[backup.snapshots]] no TOML, um único sudo rustic -P nuvem backup lê 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.
  5. Comandos do dia a dia muito mais curtos: sem precisar digitar --repo ou --password-file no terminal. Com rustic.toml apontando para local, um simples sudo rustic snapshots já lista o NAS/repositório local, e sudo rustic -P nuvem snapshots consulta 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_temporarios logo antes de criar o snapshot novo;
  • Troquei redirecionamentos com sudo (sudo cmd > arquivo, que falham fora de um shell root) por | sudo tee ... > /dev/null e mapfile -t para capturar IDs de containers e imagens do Docker com segurança;
  • Passei a controlar um contador ERROS para que o alerta de sucesso via ntfy só dispare quando ERROS -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/*.toml sem rodar backup) e --validar (que executa o fluxo completo sem upload nem docker save, conferindo show-config, --dry-run e limpeza automática via trap).

Dica extra para quem quiser ir além: o rustic també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 (nuvem e local), além de controlar facilmente modos como --validar e as notificações no ntfy.

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

Comentários (0)

Nenhum comentário publicado ainda.

Deixe um comentário

VEJA TAMBÉM