← Voltar para o Blog

rsync: as flags que salvam e a que destrói

Uma barra a mais no fim do caminho muda o destino dos arquivos. Combinada com --delete, ela apaga o que não devia. O modelo mental que evita o comando de uma linha que custa um dia.

Equipe EasyOps Cloud · · 7 min de leitura

O rsync é a ferramenta certa para copiar arquivo entre servidores, sincronizar diretório e montar backup incremental. É rápida, retoma o que parou e transfere apenas a diferença.

Também é uma das poucas ferramentas de uso diário em que um caractere a mais muda o resultado de forma drástica — e a combinação errada apaga arquivos em silêncio, com saída de sucesso.

Vale gastar dez minutos entendendo duas coisas: a barra final e o --delete.

A barra final decide o destino

Esta é a fonte da maior parte da confusão, e a regra é simples: a barra no fim da origem significa "o conteúdo de", e a ausência dela significa "o diretório".

# COM barra: copia o conteúdo de site/ para dentro de /var/www/
rsync -a /home/user/site/ servidor:/var/www/
# resultado: /var/www/index.html

# SEM barra: copia o diretório site inteiro
rsync -a /home/user/site servidor:/var/www/
# resultado: /var/www/site/index.html

No destino a barra não faz diferença — ele é sempre tratado como diretório.

O sintoma de errar é característico: os arquivos aparecem um nível abaixo do esperado, ou o site fica em /var/www/site/site/. Irritante, mas inofensivo.

Deixa de ser inofensivo quando o --delete entra na conta.

--delete: o que ele realmente faz

O --delete remove no destino tudo que não existe na origem. É o que torna a cópia um espelho fiel, e é exatamente por isso que ele é perigoso.

Combine com a barra errada e o resultado é destrutivo:

# A intenção era espelhar site/ em /var/www/site/
# A barra ausente na origem faz o rsync comparar site com
# TODO o conteúdo de /var/www/ — e apagar o que não bate
rsync -a --delete /home/user/site /var/www/

Outros servidores hospedados em /var/www/ desaparecem. O comando retorna com sucesso, porque fez exatamente o que foi pedido.

Três regras que eliminam esse risco:

  • Sempre rode com `-n` antes. Sem exceção quando houver --delete.
  • Aponte o destino o mais específico possível. /var/www/site/, nunca /var/www/.
  • Use `--delete-after` em cópia grande: as remoções acontecem no fim, depois de tudo transferir. Se a transferência falhar no meio, nada foi apagado.
rsync -avn --delete /home/user/site/ /var/www/site/

O -n é o ensaio: mostra exatamente o que seria feito, sem tocar em nada. Leia a lista de remoções antes de repetir o comando sem o -n.

As flags do dia a dia

rsync -avz --progress origem/ destino/

Cada letra:

  • -a — modo arquivo. Equivale a -rlptgoD: recursivo, preserva link simbólico, permissão, data, grupo, dono e arquivos especiais. É o que você quer em 99% dos casos.
  • -v — mostra o que está fazendo.
  • -z — comprime durante a transferência. Vale em rede lenta e atrapalha em rede rápida com arquivo já comprimido, porque gasta CPU sem reduzir nada.
  • --progress — barra por arquivo. Para o total, --info=progress2 é melhor.

Um detalhe sobre o -a: preservar dono e grupo exige privilégio no destino. Sem sudo, os arquivos chegam pertencendo ao usuário que conectou, e a aplicação pode parar de conseguir lê-los. Em transferência para servidor, isso costuma exigir:

rsync -az --rsync-path='sudo rsync' origem/ servidor:/var/www/site/

Excluir o que não deve ir

rsync -av \
  --exclude '.git/' \
  --exclude 'node_modules/' \
  --exclude '*.log' \
  --exclude '.env' \
  origem/ destino/

Para listas maiores, um arquivo:

rsync -av --exclude-from=.rsyncignore origem/ destino/

Excluir .env merece destaque. Enviar o arquivo de ambiente da sua máquina para o servidor sobrescreve as credenciais de produção com as de desenvolvimento — falha que se manifesta como aplicação que "parou de conectar no banco" logo após um deploy. O lugar certo do segredo é fora do diretório copiado, como descrito no artigo sobre onde guardar segredo.

Atenção à interação entre --exclude e --delete: por padrão, arquivo excluído é protegido no destino. Se você quer que ele também seja removido lá, precisa de --delete-excluded — e vale pensar duas vezes antes.

Sobre SSH e transferência grande

rsync -avz -e 'ssh -p 2222 -i ~/.ssh/deploy' origem/ user@servidor:/destino/

Para transferência longa, três opções mudam a experiência:

rsync -av --partial --append-verify --info=progress2 \
  arquivo-grande.tar servidor:/backup/

--partial mantém o pedaço já transferido quando a conexão cai, permitindo retomar em vez de recomeçar. --append-verify continua de onde parou, conferindo o que já existe.

Vale rodar dentro de uma sessão persistente. Uma transferência de duas horas interrompida porque o SSH caiu é frustração evitável:

tmux new -s copia
# rode o rsync aqui; Ctrl-b d para desanexar
tmux attach -t copia

Se a rede for limitada e a transferência estiver atrapalhando o serviço, dá para segurar a banda:

rsync -av --bwlimit=10M origem/ destino/

Backup incremental com espaço de um

Um uso menos conhecido e bastante útil. Com --link-dest, arquivos que não mudaram viram *hard link* para a cópia anterior em vez de serem duplicados:

#!/usr/bin/env bash
set -euo pipefail
HOJE=$(date +%F)
DEST=/backup/diario
ANTERIOR=$(ls -1d $DEST/*/ 2>/dev/null | tail -1)

rsync -a --delete \
  ${ANTERIOR:+--link-dest="$ANTERIOR"} \
  /var/www/ "$DEST/$HOJE/"

Cada diretório parece uma cópia completa e independente, mas o espaço consumido é apenas o das diferenças. Trinta dias de backup de um site de 5 GB que muda pouco podem ocupar pouco mais que 5 GB.

A limitação a conhecer: como os arquivos idênticos são o mesmo dado em disco, isso não protege contra falha do disco — é histórico, não cópia externa. A cópia externa continua sendo necessária, e o teste de restauração também.

Deploy com rsync, sem downtime

Para site estático ou aplicação sem etapa de build no servidor, o rsync já é uma ferramenta de deploy suficiente. O detalhe que evita a janela de inconsistência é não sincronizar direto no diretório servido.

Durante uma cópia, parte dos arquivos é a versão nova e parte ainda é a antiga. Se alguém acessa nesse intervalo, recebe uma mistura — CSS novo com HTML velho, ou o inverso.

A técnica é copiar ao lado e trocar um link simbólico, que é uma operação atômica:

#!/usr/bin/env bash
set -euo pipefail
VERSAO=$(date +%Y%m%d%H%M%S)
BASE=/var/www/meusite

rsync -a --delete \
  --exclude '.git/' --exclude '.env' \
  ./dist/ "servidor:$BASE/releases/$VERSAO/"

ssh servidor "ln -sfn $BASE/releases/$VERSAO $BASE/current.novo && \
              mv -T $BASE/current.novo $BASE/current"

O mv -T sobre um link simbólico é atômico: em nenhum instante o current deixa de apontar para uma versão completa. E o retorno à versão anterior vira um comando:

ssh servidor "ln -sfn $BASE/releases/ANTERIOR $BASE/current.novo && \
              mv -T $BASE/current.novo $BASE/current"

Some uma limpeza das versões antigas, senão o disco cresce a cada implantação — problema tratado no artigo sobre disco cheio:

ssh servidor "ls -1dt $BASE/releases/*/ | tail -n +6 | xargs -r rm -rf"

Se a aplicação exigir reinício depois da troca, o comando entra logo após o mv, e o service do systemd recarrega sem que ninguém precise de acesso interativo à máquina.

Verificar depois

rsync não avisa em voz alta quando algo não foi copiado. Vale conferir:

# Contagem dos dois lados
find origem/ -type f | wc -l
ssh servidor 'find /destino/ -type f | wc -l'

# Comparação real, sem transferir
rsync -avnc --delete origem/ servidor:/destino/

O -c compara por soma de verificação em vez de tamanho e data. É mais lento e é o único jeito de detectar arquivo corrompido que manteve o mesmo tamanho.

Os erros que se repetem

  • Esquecer o `-n` com `--delete`. É o único desta lista que causa perda.
  • Barra final inconsistente entre o comando de teste e o definitivo.
  • `-a` sem privilégio no destino, mudando dono dos arquivos e quebrando a aplicação.
  • `-z` em rede rápida, gastando CPU à toa.
  • Copiar `.git/` para produção, expondo o histórico do repositório pela web.
  • Rodar sem `tmux` e perder duas horas de transferência.

O primeiro é o único irreversível. Os outros custam tempo; esse custa dado — e a diferença entre os dois é um -n de dois caracteres.

Vale registrar quando não usar rsync. Ele compara arquivo a arquivo, e por isso é ineficiente para sincronizar um diretório com centenas de milhares de arquivos pequenos — a varredura sozinha demora mais que a transferência. Nesse cenário, empacotar com tar e enviar o pacote costuma ser bem mais rápido.

Também não é a ferramenta certa para banco de dados em atividade. Copiar os arquivos de dados de um Postgres ou MySQL em execução produz uma cópia inconsistente, que parece íntegra e falha ao subir. Para banco, o caminho é o dump lógico ou a ferramenta de cópia física do próprio sistema, com a consistência garantida por ele.

Fora esses dois casos, é difícil errar — desde que o -n venha antes do --delete.

Suba um servidor em minutos

Preço em real, suporte em português e dados no Brasil.

Criar conta