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.htmlNo 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 copiaSe 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.