A minha nova rotina de backup com o rustic
Há algum tempo apresentei aqui no blog a minha rotina de backup utilizando o excelente restic. Agora, trago as minhas impressões, instruções de instalação e um benchmark prático do rustic, uma reimplementação em Rust totalmente compatível com os repositórios do restic, mais veloz, sem travas e cheia de novos recursos que já estão rodando diariamente no meu desktop Manjaro.
Há algum tempo, escrevi o post "A minha rotina de backup com o restic", em que apresentei aquela que, até então, era a minha ferramenta favorita para garantir a integridade e a paz de espírito em relação aos meus dados. Recentemente, porém, resolvi colocar à prova um novo concorrente — ou melhor dizendo, um primo muito próximo e moderno — chamado rustic (repositório no GitHub e documentação oficial).
Já adianto que estou testando o rustic na prática e, até o presente momento, estou muito feliz com ele! Assim como comentei quando falei sobre o Croc e o Magic-Wormhole, sobre como usar o Atuin no histórico do shell ou na minha atualização sobre como o Atuin ficou ainda melhor (que também é escrito em Rust), é sempre empolgante encontrar um utilitário de linha de comando que resolve problemas reais do dia a dia no Linux com elegância e velocidade.
Quem criou o rustic e por quê?
Para entender por que o rustic é tão interessante, vale conhecer a história por trás dele. A ferramenta foi criada por Alexander Weiss (conhecido no GitHub e no fórum do restic como @aawsome). E ele não era um aventureiro qualquer chegando de paraquedas no universo de backups: antes de criar o rustic, o Alexander já era um dos principais contribuidores do próprio projeto restic, tendo inclusive recebido o prêmio Google Open Source Peer Bonus pelas enormes melhorias de desempenho na poda de dados (prune) e no suporte à compressão do repositório v2 que ele ajudou a implementar por lá.
Conforme o próprio autor conta no tópico oficial em que apresentou o projeto no fórum do restic e no FAQ da documentação, o que o motivou a criar o rustic foi a combinação de dois fatores:
- A vontade de dominar a linguagem Rust na prática: ele queria aprender Rust a fundo e concluiu que a melhor forma de entender os prós e contras da linguagem era reimplementar do zero um sistema complexo e crítico em desempenho que ele já conhecia nos mínimos detalhes.
- O ritmo conservador do restic: por ser um projeto maduro (escrito em Go) e muito utilizado em produção, o
resticé naturalmente conservador. Revisões de código demoravam e várias funcionalidades avançadas que a comunidade pedia há anos esbarravam na cautela dos mantenedores.
A grande sacada do Alexander foi não tentar reinventar a roda onde o restic já era perfeito. Em vez de criar um formato de repositório proprietário e incompatível, ele projetou o rustic para ler e escrever exatamente o mesmo formato criptografado especificado pelo restic.
Isto é, o rustic funciona como um substituto direto (drop-in replacement). E esse foi um dos motivos que me deixaram mais entusiasmado: ele é 100% compatível com os meus repositórios restic pré-existentes. Não precisei recriar repositórios do zero, reenviar centenas de gigabytes pela rede ou abrir mão do meu histórico de snapshots antigos. Bastou apontar o rustic para onde o restic já guardava os backups e tudo funcionou de imediato.
Como instalar o rustic
Como o projeto já ganhou bastante tração, instalar o rustic no Linux (ou mesmo no macOS e no Windows) é muito simples, conforme detalhado na página oficial de instalação:
- No Arch Linux e Manjaro: o pacote já está disponível diretamente no repositório oficial
extra/rustic, então basta rodar:
sudo pacman -S rustic
- Binário estático pré-compilado (
musl) em qualquer distribuição Linux: se você estiver em uma distribuição que ainda não empacota orustic(como certas versões de Debian/Ubuntu) ou estiver rodando um Live USB de recuperação de desastres, pode baixar o binário estático único diretamente das Releases no GitHub:
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/
- Via Cargo (ecossistema Rust): para quem já tem o
cargoinstalado (ou usa o cargo-binstall):
cargo binstall rustic-rs # ou compilando do código-fonte via crates.io: cargo install --locked rustic-rs
Velocidade, consumo de memória e meu próprio benchmark
Mantendo os quatro pilares que já elencamos no post anterior sobre o restic — repositório criptografado, deduplicado, versionado e comprimido —, onde o rustic realmente brilha logo no primeiro contato é no desempenho bruto e na economia de recursos.
Por ser escrito em Rust, sem a sobrecarga do coletor de lixo (garbage collector) do Go e com um gerenciamento de memória muito mais estrito, tanto os relatos no tópico original do fórum do restic e nas discussões sobre lentidão do prune quanto a página oficial de comparação rustic vs. restic destacam duas grandes vantagens:
- Consumo de memória RAM drasticamente menor no backup inicial e na indexação de milhões de arquivos pequenos (cenário em que o
resticfrequentemente consome gigabytes de RAM e pode sofrer com Out-Of-Memory em VPS menores). - Operações de manutenção (
check,forgeteprune) muito mais velozes, graças à leitura em lote de blobs contíguos e ao algoritmo de poda em duas fases (two-phase pruning).
Para não ficar apenas nos números de terceiros, resolvi rodar um benchmark próprio aqui no meu desktop, comparando frente a frente o restic 0.19.1 (compilado com Go 1.26.4) e o rustic 0.11.4.
Para que você saiba exatamente a origem dos números, estas são as especificações da máquina onde o teste foi executado:
- Processador (CPU): AMD Ryzen 5 8600G w/ Radeon 760M Graphics (6 núcleos / 12 threads)
- Memória RAM: 32 GB DDR5 (30 GiB disponíveis ao sistema após a reserva da iGPU)
- Armazenamento: SSD NVMe Kingston NV3 de 1 TB (
KINGSTON SNV3S1000G) com sistema de arquivos Btrfs na partição de origem e repositórios de teste emtmpfs(RAM) para isolar o gargalo de escrita e medir puramente a velocidade de leitura, deduplicação, compressão e criptografia - Sistema Operacional: Manjaro Linux 26.1.3 (Bian-May), Kernel Linux
7.2.9-1-MANJARO(x86_64)
O conjunto de dados escolhido foi a combinação das pastas /usr/share/doc e /usr/include, que juntas somam 976 MiB espalhados em 69.646 arquivos pequenos — justamente o tipo de carga que mais estressa deduplicadores de backup.
Os resultados medidos de tempo de relógio (wall clock), tempo total de CPU (user + sys) e pico de memória RAM (Maximum resident set size via wait4/rusage) foram os seguintes:
| Operação (69.646 arquivos / 976 MiB) | Ferramenta | Tempo total | Tempo de CPU | Pico de RAM | Destaque |
|---|---|---|---|---|---|
| 1º Backup inicial (a frio) | restic 0.19.1 |
2,89 s | 14,97 s | 441,2 MiB | Empate técnico em tempo |
| 1º Backup inicial (a frio) | rustic 0.11.4 |
3,06 s | 17,60 s | 167,8 MiB | 62% menos memória RAM |
| 2º Backup incremental (sem mudanças) | restic 0.19.1 |
1,78 s | 2,27 s | 76,5 MiB | — |
| 2º Backup incremental (sem mudanças) | rustic 0.11.4 |
1,05 s | 1,90 s | 143,9 MiB | 41% mais rápido |
Verificação de integridade (check) |
restic 0.19.1 |
0,86 s | 1,38 s | 91,6 MiB | — |
Verificação de integridade (check) |
rustic 0.11.4 |
0,28 s | 0,49 s | 144,0 MiB | Mais de 3× mais rápido |
Limpeza (forget --keep-last 1 --prune) |
restic 0.19.1 |
0,91 s | 1,27 s | 89,9 MiB | — |
Limpeza (forget --keep-last 1 --prune) |
rustic 0.11.4 |
0,60 s | 1,02 s | 145,5 MiB | 34% mais rápido |
Repare nos dois pontos mais importantes para a vida real:
- No primeiro backup pesado, enquanto os dois empatam na velocidade de gravação (saturando o SSD NVMe em cerca de 3 segundos), o
resticprecisou de 441,2 MiB de RAM, contra apenas 167,8 MiB dorustic(quase um terço da memória para indexar os mesmos 69 mil arquivos!). Em um repositório real com milhões de arquivos (no meu backup completo da máquina são quase 3 milhões de arquivos e 465 GiB na partição de dados), essa diferença de memória é brutal. - Já nas operações que rodam todos os dias na crontab — o backup incremental varrendo a árvore em busca de mudanças, o
checkde integridade e oforget --prune—, orusticvenceu todas com folga, sendo de 34% a mais de 200% (3×) mais rápido.
O que o rustic tem que o restic não tem?
E como um bom vendedor, eu ainda adiciono que: E NÃO É APENAS ISSO!
Conforme documentado na tabela comparativa oficial, o rustic trouxe de fábrica uma série de recursos arquiteturais que resolvem dores antigas do restic:
- Operações lock-free (sem travas no repositório): lembra que no meu post sobre o restic o primeiro comando do script era justamente um
restic unlockpara limpar travas órfãs caso um backup anterior tivesse sido interrompido? Norustic, isso simplesmente não existe. O design dele é lock-free, permitindo que múltiplos clientes façam operações simultâneas no mesmo repositório sem corromper nada e sem travar a rotina do dia seguinte. - Perfis de configuração em TOML (
rustic.toml): enquanto oresticse recusa historicamente a ter um arquivo de configuração nativo (obrigando o usuário a criar scripts ou usar ferramentas de terceiros), orusticlê perfis completos em/etc/rustic/*.tomlou~/.config/rustic/*.toml(acionados com-P <perfil>), além de suportar hooks nativos antes e depois do backup. - Suporte nativo a Cold Storage (armazenamento frio): ele permite trabalhar com repositórios divididos entre armazenamento "quente" (
--repo-hot, onde ficam apenas os metadados e índices para consulta rápida) e armazenamento "frio" de baixíssimo custo (como o AWS S3 Glacier), gerenciando automaticamente os comandos de aquecimento (warm-up) dos pacotes quando você precisa restaurar ou verificar dados. - Filtros muito mais poderosos e suporte a
.gitignore: ele entende nativamente arquivos.gitignore,.ignoreeCACHEDIR.TAG, aceita expressõesjqdiretamente para filtrar snapshots (--filter-jq), suporta sintaxe relativa ao estilo Git (comolatest~2para pegar o antepenúltimo snapshot), faz backup direto de dispositivos de bloco e realiza restauração in-place modificando apenas os blocos que realmente mudaram no destino. - Tamanho dinâmico de pacotes (pack sizes): evita aquele problema clássico de repositórios antigos do
resticacumularem centenas de milhares de arquivinhos de poucos megabytes, escalando o tamanho dos pacotes conforme o repositório cresce.
Comandos exclusivos do rustic
Ao comparar o --help das duas ferramentas, encontramos vários comandos que existem nativamente no rustic e não existem no restic:
Comando no rustic |
O que ele faz |
|---|---|
rustic repoinfo |
Exibe um raio-X completo do repositório: quantidade e tamanho dos arquivos de índice e de dados (packs), taxa de compressão, blobs utilizados vs. marcados para exclusão. |
rustic merge |
Mescla múltiplos snapshots em um único snapshot consolidado (excelente para juntar backups feitos em partes). |
rustic webdav |
Sobe instantaneamente um servidor WebDAV local para você navegar pelos snapshots pelo navegador ou gerenciador de arquivos, sem depender de FUSE (mount). |
rustic config / show-config |
Permite alterar configurações internas do repositório (como nível de compressão, versão e tamanho de pacotes) e visualizar a configuração final carregada dos perfis TOML. |
rustic snapshots -i / ls -i |
Abre uma interface visual interativa no próprio terminal (TUI) para navegar pelas datas, árvores de pastas, comparar versões e selecionar arquivos para restaurar sem sair do shell. |
rustic docs / completions |
Abre a documentação local e gera os scripts de autocompletar para Bash, Zsh ou Fish. |
A minha rotina diária no Manjaro e o script .restic.sh
Apesar de todo esse entusiasmo, quem acompanha o blog sabe que sou cauteloso com infraestrutura crítica. Por isso, neste primeiro momento, estou utilizando o rustic como um teste prático apenas na gestão dos backups do meu desktop pessoal com Manjaro Linux (como já comentei na página Quem sou eu, o Manjaro é a minha distro de escolha no desktop há anos).
O script roda todos os dias e, por pura preguiça de alterar os atalhos e agendamentos que já existiam, ele continua atendendo pelo irônico nome de /home/giuliano/.restic.sh — embora, por dentro, não haja mais uma única chamada ao restic.
A minha rotina evoluiu bastante desde aquele script simples do primeiro post. Hoje, ela congela a partição raiz em Btrfs criando um snapshot somente-leitura (/.snap_root) para garantir consistência absoluta do sistema operacional em uso, exporta as imagens e metadados dos containers Docker, faz o backup separado por tags em dois repositórios (um disco local e um remoto na nuvem via rclone), aplica a política de retenção, verifica a integridade com check e ainda me avisa no celular via ntfy se tudo correu bem ou se houve alguma falha.
Abaixo está uma versão simplificada e com caminhos anonimizados do meu ~/.restic.sh atual:
#!/usr/bin/env bash # ============================================================================== # SCRIPT DE BACKUP COMPLETO (SISTEMA + DADOS + DOCKER) - RUSTIC & BTRFS # ============================================================================== LOG_FILE="/home/usuario/rustic_backup_completo.log" PASSWORD_FILE="/caminho/seguro/.senha.txt" NTFY_URL="https://ntfy.exemplo.work/meu-topico-secreto" notificar_erro() { local mensagem="$1" echo "[ALERTA] $mensagem" curl -s -o /dev/null \ -H "Title: Falha no Backup (Desktop)" \ -H "Tags: rotating_light,skull" \ -d "$mensagem" "$NTFY_URL" } notificar_sucesso() { 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" } # 1. Congelar o sistema raiz (Snapshot Btrfs Read-Only) sudo btrfs subvolume delete /.snap_root 2>/dev/null sudo btrfs subvolume snapshot -r / /.snap_root || notificar_erro "Falha ao criar snapshot Btrfs." # 2. Exportação de metadados e imagens do Docker DOCKER_BKP_DIR="/var/tmp/docker_bkp_temp" sudo mkdir -p "$DOCKER_BKP_DIR" if [ -n "$(sudo docker ps -aq)" ]; then sudo docker ps -a > "$DOCKER_BKP_DIR/containers_list.txt" sudo docker inspect $(sudo docker ps -aq) > "$DOCKER_BKP_DIR/containers_inspect.json" 2>/dev/null fi if [ -n "$(sudo docker images -q)" ]; then sudo docker save $(sudo docker images -q) -o "$DOCKER_BKP_DIR/docker_images.tar" 2>/dev/null fi # 3. Repositórios: Nuvem (Google Drive via rclone) e Disco Local REPOS=( "rclone:meu_remoto:backup/desktop" "/mnt/armazem/backup" ) # 4. Execução para cada repositório (note que não precisamos mais do 'unlock'!) for REPO in "${REPOS[@]}"; do # 4.1 Backup do Sistema Operacional (a partir do snapshot Btrfs congelado) cd /.snap_root || notificar_erro "Não foi possível acessar /.snap_root" sudo rustic --password-file="$PASSWORD_FILE" -r "$REPO" backup . \ --tag "sistema-raiz" \ --one-file-system \ --glob "!./tmp/*" \ --glob "!./var/tmp/*" \ --glob "!./timeshift" || notificar_erro "Erro no backup do SISTEMA RAIZ em: $REPO" # 4.2 Backup exclusivo da pasta Sync cd / sudo rustic --password-file="$PASSWORD_FILE" -r "$REPO" backup /home/usuario/Sync \ --tag "pasta-sync" \ --exclude-if-present CACHEDIR.TAG \ --glob "!**/.cache" || notificar_erro "Erro no backup da pasta SYNC em: $REPO" # 4.3 Backup dos Dados Pessoais sudo rustic --password-file="$PASSWORD_FILE" -r "$REPO" backup /home /armazem \ --tag "dados-pessoais" \ --glob "!/armazem/backup" \ --exclude-if-present CACHEDIR.TAG \ --glob "!**/.cache" || notificar_erro "Erro no backup dos DADOS PESSOAIS em: $REPO" # 4.4 Backup do Docker (Volumes, Imagens e Configs) sudo rustic --password-file="$PASSWORD_FILE" -r "$REPO" backup \ /var/lib/docker/volumes "$DOCKER_BKP_DIR" \ --tag "docker-data" || notificar_erro "Erro no backup do DOCKER em: $REPO" # 4.5 Política de retenção, poda (prune) e verificação de integridade (check) sudo rustic --password-file="$PASSWORD_FILE" -r "$REPO" forget \ --keep-daily 7 --keep-weekly 2 --keep-monthly 1 --keep-tags keep --prune \ || notificar_erro "Erro no forget/prune em: $REPO" sudo rustic --password-file="$PASSWORD_FILE" -r "$REPO" check \ || notificar_erro "ATENÇÃO: Falha de INTEGRIDADE em: $REPO" done # 5. Limpeza final cd / sudo btrfs subvolume delete /.snap_root sudo rm -rf "$DOCKER_BKP_DIR" notificar_sucesso
Um detalhe importante para quem for migrar um script do restic para o rustic: embora os comandos principais (backup, forget, prune, check, restore) tenham os mesmos nomes, algumas flags de exclusão e retenção mudam. Na minha migração, precisei trocar --exclude="pasta" por --glob "!pasta", substituir --exclude-caches por --exclude-if-present CACHEDIR.TAG e ajustar --keep-tag (no singular) para --keep-tags (no plural).
Em compensação, a saída do rustic forget no log é um espetáculo à parte, desenhando tabelas super claras agrupadas por host, tags e caminhos, mostrando exatamente o motivo pelo qual cada snapshot foi mantido (daily, weekly, monthly, tags) ou removido:
[INFO] snapshots for (host [desktop], label [], paths [/armazem,/home]): | ID | Time | Host | Label | Tags | Paths | Action | Reason | |----------|---------------------|---------|-------|----------------|----------|--------|---------| | c4e70438 | 2026-11-12 08:29:00 | desktop | | dados-pessoais | /armazem | keep | daily | | | | | | | /home | | weekly | | | | | | | | | monthly | | 6bee4c53 | 2026-11-12 03:04:49 | desktop | | dados-pessoais | /armazem | remove | | | | | | | | /home | | | | be401c6e | 2026-11-11 13:12:32 | desktop | | dados-pessoais | /armazem | keep | daily |
Nem tudo são flores: rclone vs. OpenDAL, documentação e o ecossistema
Como não existe ferramenta perfeita, é importante apontar onde o rustic ainda fica devendo em relação ao veterano restic.
1. Por que continuo usando o rclone para o Google Drive (e as limitações do OpenDAL)
Se você reparou no script acima, o meu repositório na nuvem continua sendo chamado via rclone:meu_remoto:backup/desktop. Mas o rustic não tem suporte nativo a dezenas de serviços de nuvem?
Mais ou menos. O rustic delega o acesso à maioria dos backends remotos para a biblioteca Apache OpenDAL (Open Data Access Layer), usando a sintaxe opendal:<servico> (como opendal:s3 ou opendal:gdrive), além de manter a integração direta com o rclone.
Eu optei deliberadamente por não usar o suporte nativo a Google Drive do rustic e continuar passando pelo rclone. O motivo é simples: eu confio muito mais no meu repositório do Google Drive pré-configurado no rclone — que tem anos de estrada lidando com os limites de requisição (rate limits), renovação de tokens OAuth2, listagens recursivas e peculiaridades da API da Google — do que na camada genérica do OpenDAL.
E aqui entram outras desvantagens da cobertura dessa biblioteca: embora o OpenDAL funcione muito bem para armazenamento de objetos padrão como o Amazon S3, não há paridade real de maturidade e recursos entre todos os backends listados. Basta olhar a própria tabela de serviços suportados pelo rustic: o suporte a SFTP via OpenDAL sequer funciona no Windows, o suporte a FTP precisou ser desativado por problemas de compilação ("FTP is temporarily deactivated, due to build issues"), e em serviços de nuvem voltados ao consumidor final (como o próprio Google Drive, OneDrive ou Dropbox) usuários frequentemente esbarram em gargalos severos de performance ao listar milhares de pacotes recursivamente, limitações em operações atômicas ou inconsistências sutis na leitura de metadados durante o check. O rclone, por outro lado, é uma rocha.
2. Uma documentação que terceiriza o trabalho
Outro ponto em que o manual do rustic perde para a documentação do restic é na abrangência. O manual do restic é direto, completo e autocontido: tudo o que você precisa saber para autenticar e configurar qualquer backend está explicado passo a passo na própria página.
Já a documentação do rustic, em vários momentos — especialmente na página de configuração de serviços remotos (services.html), quando você quer configurar o suporte ao Google Drive, Backblaze B2, SFTP ou outros remotos via OpenDAL —, traz apenas um exemplo básico de S3 e simplesmente manda você ler a documentação técnica da biblioteca em docs.rs/opendal/latest/opendal/services/ para descobrir quais parâmetros colocar no bloco [repository.options], em vez de já trazer as informações e exemplos prontos na própria página da documentação. Para quem só quer configurar o backup rápido sem ter que garimpar nomes de métodos e structs na documentação de uma crate do Rust (como a opendal::services::Gdrive), isso é um incômodo desnecessário.
3. Falta um "Awesome Rustic"
Por fim, a última desvantagem (que é natural pela diferença de idade entre os projetos) está no ecossistema ao redor da ferramenta.
Quando escrevi sobre o restic, fiz questão de recomendar o excelente repositório awesome-restic, que reúne dezenas de interfaces gráficas (como o Backrest, o Restic Browser e o NPBackup), gerenciadores de perfis (como o resticprofile), exportadores de métricas e scripts criados pela comunidade que expandem enormemente a gestão do backup.
Embora o rustic já traga embutidas algumas coisas que no restic dependiam de projetos externos (como os perfis em TOML, o envio nativo de métricas para Prometheus e OpenTelemetry, e a TUI interativa no terminal), ainda sentimos falta de ferramentas gráficas e utilitários de terceiros feitos especificamente para expandir a gestão dele. Graças à compatibilidade do formato de repositório, várias ferramentas do restic conseguem ler um repositório gerenciado pelo rustic, mas ainda faz falta um verdadeiro "awesome-rustic".
Conclusão
Mesmo mantendo o rustic por enquanto em caráter de teste diário no meu desktop Manjaro e preferindo a segurança do bom e velho rclone para conversar com o Google Drive, o saldo até aqui é extremamente positivo. Ter um binário único em Rust, muito mais rápido nas verificações diárias, consumindo uma fração da memória RAM, livre daquelas travas irritantes (unlock) e 100% compatível com os repositórios restic que eu já tinha é o melhor dos dois mundos.
Se você já usa o restic no seu homelab ou desktop Linux, recomendo fortemente instalar e testar o rustic (GitHub) em um repositório existente — fazendo antes um teste com --dry-run, é claro. É uma daquelas ferramentas que mostram como o ecossistema de código aberto evolui quando alguém resolve coçar a própria coceira com competência.