Javier Valencia Javier Valencia
Rsync: el comando que uso todos los días

Rsync: el comando que uso todos los días

Javier Valencia · · 2 min de lectura · 2 visitas · DevOps
devops tutorial rsync backup linux

Si tuviera que quedarme con un solo comando de Linux, sería rsync. Lo uso para subir código a servidores, para hacer backups, para sincronizar directorios entre máquinas, para mover terabytes de datos sin perder nada. Es una de esas herramientas que llevan décadas existiendo, que no son llamativas, y que simplemente funcionan.

Lo básico

Rsync: el comando que uso todos los días

Rsync sincroniza ficheros de un origen a un destino. La gracia es que solo transfiere lo que ha cambiado, no todo:

rsync -avz ./mi-proyecto/ servidor:/opt/app/

Los flags que uso siempre:

  • -a (archive): preserva permisos, timestamps, links simbólicos, propietarios. Es el flag más importante.
  • -v (verbose): muestra qué ficheros se transfieren.
  • -z (compress): comprime los datos durante la transferencia. Útil con conexiones lentas.

Esa barra final en ./mi-proyecto/ es importante. Con la barra, rsync copia el contenido del directorio. Sin la barra, copia el directorio en sí. La diferencia entre que tus ficheros acaben en /opt/app/ o en /opt/app/mi-proyecto/.

Dry-run: mira antes de tocar

El flag más infravalorado de rsync:

rsync -avzn ./local/ servidor:/opt/app/

El -n (dry-run) muestra exactamente qué haría sin hacer nada. Es la diferencia entre un deploy tranquilo y un incidente a las tres de la mañana. Siempre hago un dry-run antes de un rsync real en producción. Siempre.

Exclusiones

Rsync: el comando que uso todos los días

Casi nunca quieres sincronizar todo. El flag --exclude filtra lo que no debe ir:

rsync -avz \
  --exclude='.git' \
  --exclude='node_modules' \
  --exclude='*.log' \
  --exclude='.env' \
  ./proyecto/ servidor:/opt/app/

Para proyectos con muchas exclusiones, puedes usar un fichero:

# .rsync-exclude
.git
node_modules
tmp
*.log
.env
.env.*
__pycache__
rsync -avz --exclude-from='.rsync-exclude' ./proyecto/ servidor:/opt/app/

--delete: el flag peligroso (y necesario)

Por defecto, rsync solo añade y actualiza. Si borras un fichero en local, sigue existiendo en el servidor. El flag --delete elimina del destino lo que ya no está en el origen:

rsync -avz --delete ./proyecto/ servidor:/opt/app/

Este flag es necesario para mantener el destino como un espejo exacto del origen, pero hay que usarlo con cuidado. Si te equivocas de directorio, puedes borrar cosas que no debías. Por eso el dry-run es sagrado:

rsync -avzn --delete ./proyecto/ servidor:/opt/app/
# Revisar la salida
# Si todo está bien:
rsync -avz --delete ./proyecto/ servidor:/opt/app/

Backups incrementales

Rsync: el comando que uso todos los días

Rsync es perfecto para backups porque solo copia lo que ha cambiado. Con --link-dest puedes hacer backups incrementales que parecen completos pero ocupan una fracción del espacio:

#!/bin/bash
DATE=$(date +%Y-%m-%d)
LATEST=/backups/latest
DEST=/backups/$DATE

rsync -avz --delete --link-dest=$LATEST servidor:/opt/app/data/ $DEST/
ln -snf $DEST $LATEST

Cada backup es un directorio completo con todos los ficheros. Pero los ficheros que no han cambiado son hard links al backup anterior, así que no ocupan espacio extra. Puedes entrar en cualquier backup y ver el estado completo del sistema en ese momento, pero el almacenamiento total es solo la suma de los cambios.

Limitar el ancho de banda

En producción, no quieres que un rsync de 50GB sature la conexión:

rsync -avz --bwlimit=10000 ./datos/ servidor:/opt/datos/

--bwlimit=10000 limita a 10MB/s. Suficiente para que la transferencia avance sin afectar al tráfico real del servidor.

Reanudar transferencias

Si una transferencia grande se interrumpe, rsync puede reanudarla:

rsync -avz --partial --progress ./imagen.iso servidor:/tmp/

--partial mantiene los ficheros parcialmente transferidos en vez de borrarlos. --progress muestra el progreso de cada fichero. La próxima vez que ejecutes el comando, rsync retoma donde lo dejó.

Para ficheros muy grandes, usa --append-verify:

rsync -avz --append-verify ./dump.sql.gz servidor:/backups/

Continúa la transferencia desde donde se quedó y verifica el checksum al final.

SSH con puerto personalizado

Si tu servidor SSH usa un puerto diferente al 22:

rsync -avz -e 'ssh -p 2222' ./proyecto/ servidor:/opt/app/

También puedes especificar una clave SSH concreta:

rsync -avz -e 'ssh -i ~/.ssh/id_deploy' ./proyecto/ servidor:/opt/app/

Mi script de deploy

Este es el script que uso para desplegar este blog:

#!/bin/bash
set -euo pipefail

SERVER="[email protected]"
DEST="/opt/blog"

echo "==> Dry-run..."
rsync -avzn --delete \
  --exclude='.git' \
  --exclude='go.*' \
  --exclude='cmd' \
  --exclude='internal' \
  --exclude='*.go' \
  --exclude='deploy' \
  --exclude='.env' \
  --exclude='content/views.json' \
  ./ $SERVER:$DEST/

read -p "¿Continuar? (s/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Ss]$ ]]; then
  rsync -avz --delete \
    --exclude='.git' \
    --exclude='go.*' \
    --exclude='cmd' \
    --exclude='internal' \
    --exclude='*.go' \
    --exclude='deploy' \
    --exclude='.env' \
    --exclude='content/views.json' \
    ./ $SERVER:$DEST/
  echo "==> Reiniciando servicio..."
  ssh $SERVER 'systemctl restart blog'
  echo "==> Hecho."
fi

Primero dry-run, luego confirmación, luego el rsync real y reinicio del servicio. Simple, predecible y a prueba de errores.

Conclusión

Rsync lleva existiendo desde 1996 y no ha necesitado reinventarse porque hace bien lo que hace. No es llamativo, no tiene una interfaz web bonita, no tiene un SaaS detrás. Es un comando que copia ficheros de forma inteligente. Y eso, en el día a día de un administrador de sistemas o un desarrollador que gestiona servidores, es más valioso que cualquier herramienta moderna con quinientas estrellas en GitHub.