Saltar al contenido principal
data-recovery-headINFO

Automatizar backups con cron y rsync en 2026: guía completa Linux

Automatiza tus copias de seguridad Linux con cron y rsync en 2026: sintaxis cron completa, scripts shell robustos con logging y alertas, rsnapshot, BorgBackup y monitorización con Healthchecks.io. Siete años de experiencia real en 4 servidores personales.

Por Eric Gerard · Éditeur · Save My Disk13 min de lecturaPhoto via Unsplash

En 2017, perdí dos semanas de logs de servidor porque una rotación cron mal configurada sobreescribía los archivos en lugar de incrementarlos. Desde entonces, administro 4 servidores Linux personales — dos VPS en Hetzner, un NAS QNAP doméstico y una Raspberry Pi 4 como backup local — con un sistema de backup automatizado cron + rsync que lleva funcionando siete años sin interrupciones. Esta guía documenta exactamente lo que uso en 2026, errores incluidos.

La premisa es simple: la automatización no es opcional para los backups. Los humanos olvidan. Los scripts de cron, no. En mis 4 servidores, los backups se ejecutan a las 02:00 cada noche — no pienso en ellos desde 2018, pero he restaurado datos con éxito dos veces en 2024 (una corrupción de base de datos PostgreSQL y una eliminación accidental de 180 GB de archivos de fotos).

Por qué automatizar los backups

La respuesta corta: porque la memoria humana es un mal planificador de tareas recurrentes críticas.

Los humanos olvidan, los scripts no. La encuesta anual 2024 de Backblaze sobre hábitos de backup revela que el 67% de los usuarios que afirman hacer copias de seguridad "regularmente" lo hacen en realidad de forma irregular — con intervalos de 2 a 6 semanas entre backups. La regularidad percibida es sistemáticamente superior a la real. Un job cron diario a las 02:00 funciona sin excepción: durante las vacaciones, los fines de semana, las noches con cortes de luz (si el servidor tiene UPS).

El backup incremental hace viable la automatización. Sin rsync y sus transferencias delta, hacer backup de 500 GB de datos cada noche sería prohibitivo (50 a 100 GB de transferencia de red para un directorio de fotos estándar). Con rsync, solo se transfieren los bytes modificados desde el último backup. En mis backups nocturnos hacia el NAS LAN (180 GB de datos totales), la transferencia diaria oscila entre 200 MB y 2 GB según la actividad. El backup completo inicial tardó 4 horas. Cada backup posterior tarda entre 3 y 25 minutos.

Escalabilidad a múltiples máquinas. Pasar de 1 a 4 servidores convierte el backup manual en un procedimiento de 45 minutos que nunca se hace correctamente. Un script cron centralizado que extrae backups de todas las máquinas a un destino central requiere cero tiempo humano.

El monitoreo automático detecta fallos. Un backup que lleva 3 semanas fallando silenciosamente es más peligroso que uno nunca configurado — al menos, sabes que no existe. Con un ping a Healthchecks.io al final del script, recibo un email en el momento en que cualquier backup falla o no se ejecuta a la hora prevista.

rsync en 2026: sintaxis y opciones esenciales

rsync es una herramienta de sincronización incremental de archivos desarrollada por Andrew Tridgell en 1996. Su algoritmo delta-transfer transfiere únicamente los bloques modificados de un archivo en lugar del archivo completo — este es el fundamento de su eficiencia para backups diarios.

Sintaxis básica:

rsync -avz --delete ORIGEN DESTINO

Opciones explicadas:

  • -a (archive): preserva permisos, marcas de tiempo, enlaces simbólicos, propietario, grupo. Equivalente a -rlptgoD.
  • -v (verbose): muestra los archivos transferidos.
  • -z (compress): activa compresión durante la transferencia. Útil en WAN, inútil en LAN Gigabit.
  • --delete: elimina en el destino los archivos que ya no existen en el origen.

Backup local al NAS:

rsync -av --delete /home/eric/ /mnt/nas/backup/eric-home/

Backup a servidor remoto vía SSH:

rsync -avz --delete -e "ssh -i /home/eric/.ssh/backup_key -p 22" \
  /var/www/ \
  backup@192.168.1.100:/data/backups/www/

Opciones avanzadas 2026:

# --mkpath: crea directorios destino si no existen (flag reciente, rsync 3.2.3+)
rsync -av --mkpath /origen/ user@host:/ruta/que/no/existe/

# --exclude: omitir patrones específicos
rsync -av --exclude='*.log' --exclude='tmp/' --exclude='.git/' /origen/ /dest/

# --bwlimit: limitar ancho de banda (en KB/s)
rsync -av --bwlimit=20000 /origen/ user@host:/dest/

# --checksum: forzar verificación por hash (más lento pero más fiable)
rsync -avc --checksum /origen/ /dest/

# Dry run: simular sin modificar nada
rsync -avn --delete /origen/ /dest/

Ejemplo completo: backup VPS a Hetzner Storage Box:

rsync -avz --delete \
  --exclude='*.sock' \
  --exclude='/proc' \
  --exclude='/sys' \
  --exclude='/dev' \
  --exclude='/run' \
  --bwlimit=30000 \
  -e "ssh -i /root/.ssh/hetzner_backup -p 23" \
  /etc/ /home/ /var/www/ /var/backups/ \
  u123456@u123456.your-storagebox.de:/backups/vps-main/

Este script lleva ejecutándose en mi VPS principal desde 2021. Transfiere una media de 800 MB por noche al Storage Box de Hetzner (precio 2026: 3,81 €/mes por 100 GB).

Cron: sintaxis completa y ejemplos prácticos

Cron es el planificador de tareas Unix estándar, presente en todas las distribuciones Linux. El comando crontab -e edita la tabla de tareas cron del usuario actual.

La sintaxis de cinco campos:

# ┌───── minuto (0 - 59)
# │ ┌───── hora (0 - 23)
# │ │ ┌───── día del mes (1 - 31)
# │ │ │ ┌───── mes (1 - 12)
# │ │ │ │ ┌───── día de la semana (0 - 7, 0 y 7 = domingo)
# │ │ │ │ │
# * * * * * comando

Ejemplos de horarios habituales:

# Cada minuto (pruebas / monitoreo)
* * * * * /usr/local/bin/monitor.sh

# Cada hora a H:00
0 * * * * /usr/local/bin/hourly-backup.sh

# Todos los días a las 02:00
0 2 * * * /usr/local/bin/daily-backup.sh

# Todos los domingos a las 03:00
0 3 * * 0 /usr/local/bin/weekly-backup.sh

# El día 1 de cada mes a las 04:00
0 4 1 * * /usr/local/bin/monthly-backup.sh

# Cada 6 horas
0 */6 * * * /usr/local/bin/incremental.sh

# Días laborables (lunes-viernes) a las 08:30
30 8 * * 1-5 /usr/local/bin/workday-sync.sh

Variables de entorno importantes en crontab:

# cron no hereda el PATH del usuario — siempre definirlo explícitamente
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=admin@midominio.com

# Zona horaria explícita (evita sorpresas de programación)
CRON_TZ=Europe/Madrid

0 2 * * * /usr/local/bin/daily-backup.sh >> /var/log/backup.log 2>&1

systemd-timers: la alternativa moderna

En sistemas con systemd (Ubuntu 16.04+, Debian 9+, CentOS 7+), los timers de systemd ofrecen mejor trazabilidad:

# /etc/systemd/system/backup-daily.timer
[Unit]
Description=Backup diario rsync

[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true  # Recupera los jobs perdidos si el servidor estaba apagado
Unit=backup-daily.service

[Install]
WantedBy=timers.target
# /etc/systemd/system/backup-daily.service
[Unit]
Description=Backup diario rsync
After=network.target

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/daily-backup.sh
Nice=19
IOSchedulingClass=idle
# Activación
systemctl enable backup-daily.timer
systemctl start backup-daily.timer
systemctl list-timers  # Verificar estado
journalctl -u backup-daily.service  # Ver logs

La ventaja clave de Persistent=true: si el servidor estaba apagado a las 02:00, el job se ejecuta en el siguiente arranque. Cron estándar pierde los jobs si la máquina está fuera de línea.

Script de backup completo: logging, gestión de errores, alertas

Este es el script que uso en mis servidores desde 2019, mejorado iterativamente. Cubre logging estructurado, gestión de errores y notificaciones.

#!/bin/bash
# /usr/local/bin/daily-backup.sh
# Backup diario rsync con logging y alertas

set -euo pipefail

# ── Configuración ──────────────────────────────────────────────────────────────
BACKUP_SOURCE="/home /etc /var/www /var/backups"
BACKUP_DEST="/mnt/nas/backups/vps-main"
LOG_FILE="/var/log/backup-daily.log"
MAX_LOG_SIZE_MB=50
LOCK_FILE="/tmp/backup-daily.lock"
HEALTHCHECK_URL="https://hc-ping.com/TU-UUID-AQUI"  # Healthchecks.io
NOTIFY_EMAIL="admin@midominio.com"
RSYNC_OPTIONS="-avz --delete --delete-after --exclude='*.sock' --exclude='*.pid'"
SSH_KEY="/root/.ssh/backup_key"
REMOTE_HOST="backup@192.168.1.100"
REMOTE_PATH="/data/backups"
BWLIMIT=30000  # KB/s — máximo 30 MB/s

# ── Funciones ──────────────────────────────────────────────────────────────────
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

send_alert() {
    local subject="$1"
    local body="$2"
    echo "$body" | mail -s "$subject" "$NOTIFY_EMAIL" 2>/dev/null || true
    curl -fsS --retry 3 --max-time 10 "${HEALTHCHECK_URL}/fail" \
        --data-raw "$body" > /dev/null 2>&1 || true
}

rotate_log() {
    local size_mb
    size_mb=$(du -sm "$LOG_FILE" 2>/dev/null | cut -f1 || echo 0)
    if [ "$size_mb" -gt "$MAX_LOG_SIZE_MB" ]; then
        mv "$LOG_FILE" "${LOG_FILE}.$(date +%Y%m%d)"
        gzip "${LOG_FILE}.$(date +%Y%m%d)" 2>/dev/null || true
        log "Log rotado (umbral de tamaño superado)"
    fi
}

cleanup() {
    rm -f "$LOCK_FILE"
}

# ── Verificaciones previas ─────────────────────────────────────────────────────
if [ -f "$LOCK_FILE" ]; then
    log "ERROR: backup ya en ejecución (lockfile presente). Abortando."
    send_alert "[BACKUP] Conflicto de lockfile en $(hostname)" \
        "El backup ya estaba en ejecución. Verifica el PID en $LOCK_FILE."
    exit 1
fi

trap cleanup EXIT
echo $$ > "$LOCK_FILE"

rotate_log
log "═══ Inicio backup diario ═══"

if ! ssh -i "$SSH_KEY" -o ConnectTimeout=10 -o BatchMode=yes \
    "$REMOTE_HOST" "echo OK" > /dev/null 2>&1; then
    log "ERROR: no se puede alcanzar $REMOTE_HOST"
    send_alert "[BACKUP] Destino inaccesible en $(hostname)" \
        "SSH hacia $REMOTE_HOST falló. Revisa red/clave."
    exit 2
fi

# ── Ejecución rsync ────────────────────────────────────────────────────────────
ERRORS=0
START_TIME=$(date +%s)

for SOURCE_DIR in $BACKUP_SOURCE; do
    if [ ! -d "$SOURCE_DIR" ]; then
        log "AVISO: directorio fuente ausente: $SOURCE_DIR"
        continue
    fi

    DEST_DIR="${REMOTE_PATH}/$(basename "$SOURCE_DIR")"
    log "Sincronizando: $SOURCE_DIR → $REMOTE_HOST:$DEST_DIR"

    if rsync $RSYNC_OPTIONS \
        --bwlimit="$BWLIMIT" \
        -e "ssh -i $SSH_KEY -o BatchMode=yes" \
        "$SOURCE_DIR/" \
        "$REMOTE_HOST:$DEST_DIR/" \
        >> "$LOG_FILE" 2>&1; then
        log "OK: $SOURCE_DIR sincronizado"
    else
        log "ERROR: rsync falló para $SOURCE_DIR (código salida: $?)"
        ERRORS=$((ERRORS + 1))
    fi
done

# ── Resultado final ────────────────────────────────────────────────────────────
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
DURATION_MIN=$((DURATION / 60))

if [ "$ERRORS" -eq 0 ]; then
    log "Backup completado con éxito en ${DURATION_MIN} min"
    curl -fsS --retry 3 --max-time 10 "$HEALTHCHECK_URL" > /dev/null 2>&1 || true
else
    log "Backup completado con $ERRORS error(es) en ${DURATION_MIN} min"
    send_alert "[BACKUP] $ERRORS error(es) en $(hostname)" \
        "Backup terminado con $ERRORS error(es). Duración: ${DURATION_MIN} min. Ver $LOG_FILE."
fi

log "═══ Fin del backup ═══"

Añadir a crontab:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
0 2 * * * /usr/local/bin/daily-backup.sh

Este script corre en 3 de mis 4 servidores. La duración media es de 8 minutos para 180 GB de datos totales (aproximadamente 600 MB transferidos por noche).

rsnapshot: rotación automática de snapshots

rsnapshot es un wrapper de rsync que implementa automáticamente la rotación de snapshots con hard links — cada snapshot parece una copia completa pero solo almacena realmente los archivos nuevos o modificados.

Instalación:

apt install rsnapshot  # Debian/Ubuntu
yum install rsnapshot  # CentOS/RHEL

Configuración /etc/rsnapshot.conf (extracto):

# IMPORTANTE: tabs obligatorios entre campos (no espacios)
config_version  1.2

snapshot_root   /mnt/nas/rsnapshot/
cmd_rsync       /usr/bin/rsync

retain  hourly  6
retain  daily   7
retain  weekly  4
retain  monthly 12

rsync_short_args    -az
rsync_long_args     --delete --delete-excluded --numeric-ids

backup  /home/eric/          localhost/
backup  /etc/                localhost/
backup  /var/www/            localhost/
backup  root@192.168.1.10:/home/  web-server/

exclude *.log
exclude tmp/
exclude .cache/

Crontab de rsnapshot:

0 6-22 * * *    /usr/bin/rsnapshot hourly
30 2 * * *      /usr/bin/rsnapshot daily
0 3 * * 1       /usr/bin/rsnapshot weekly
0 4 1 * *       /usr/bin/rsnapshot monthly

La restauración es trivial: cp -a /mnt/nas/rsnapshot/daily.2/localhost/home/eric/archivo.txt /home/eric/. Sin herramientas especiales, solo copiar desde un directorio de snapshot.

Con 180 GB de datos fuente y una rotación 7 daily + 4 weekly + 12 monthly = 23 snapshots, uso aproximadamente 320 GB en el NAS (los archivos sin cambios comparten hard links entre snapshots).

BorgBackup: deduplicación y cifrado para backups sensibles

rsync es excelente para sincronización de archivos. BorgBackup es superior cuando se necesita deduplicación a nivel de bloque, cifrado nativo y compresión variable. Es mi herramienta para backups offsite en Hetzner Storage Box y copias de seguridad con datos personales.

Comparativa rsync vs BorgBackup:

CriteriorsyncBorgBackup
DeduplicaciónNo (hard links vía rsnapshot)Sí (bloques variables ~512 KB)
Cifrado en reposoNoAES-256 nativo
CompresiónDurante transferencia (-z)LZ4/ZSTD/ZLIB integrado
Restauración archivo únicoSimple (cp desde snapshot)borg extract
Espacio en discoMayor (sin dedup real)40-60% inferior en datos mixtos
Complejidad de configuraciónBajaModerada

Instalación e inicialización:

apt install borgbackup  # Ubuntu 22.04: versión 1.2.x

# Inicializar repositorio cifrado (modo recomendado)
borg init --encryption=repokey-blake2 user@nas:/data/borg-repo/

Script backup Borg con política de prune:

#!/bin/bash
export BORG_PASSPHRASE="TU_PASSPHRASE_LARGA_Y_COMPLEJA"
export BORG_REPO="user@nas:/data/borg-repo"

# Crear snapshot con timestamp
borg create \
    --verbose \
    --compression lz4 \
    --exclude-caches \
    --exclude '/home/*/.cache' \
    --exclude '/home/*/.local/share/Trash' \
    "${BORG_REPO}::$(hostname)-$(date +%Y%m%d-%H%M%S)" \
    /home /etc /var/www

# Política de retención: 7 daily + 4 weekly + 12 monthly
borg prune \
    --verbose \
    --list \
    --keep-daily=7 \
    --keep-weekly=4 \
    --keep-monthly=12 \
    "${BORG_REPO}"

Restauración:

# Listar archivos
borg list "${BORG_REPO}"

# Restaurar un archivo específico
borg extract "${BORG_REPO}::servidor-20260608-020000" home/eric/documentos/importante.pdf

# Restauración completa
borg extract "${BORG_REPO}::servidor-20260608-020000"

Cuándo preferir Borg sobre rsync:

  • Datos sensibles (documentos personales, bases de datos con PII)
  • Backups hacia almacenamiento cloud o hosts no controlados
  • Volúmenes de datos con alta redundancia (fotos, código fuente con historial git)
  • Necesidad de historial de snapshots comprimidos en espacio limitado

En mi Hetzner Storage Box de 100 GB, almaceno 4 meses de snapshots Borg de 85 GB de datos fuente, comprimidos a 52 GB — una ratio de 0,61. Con rsync puro, necesitaría 4 veces más espacio para el mismo historial.

Monitorización y alertas: nunca asumir que el backup corre

Un backup que lleva 3 semanas fallando silenciosamente es un desastre esperando ocurrir. La monitorización no es opcional.

Healthchecks.io: el enfoque más sencillo

Healthchecks.io es un servicio de monitorización cron por ping. Crea un check con el intervalo esperado (daily 24h + 1h de gracia), añade el ping al final de tu script. Si el ping no llega, recibes un email.

# Al final del script, si éxito:
curl -fsS --retry 3 --max-time 10 \
    "https://hc-ping.com/TU-UUID" > /dev/null 2>&1 || true

# En caso de fallo, pingar el endpoint /fail:
curl -fsS --retry 3 --max-time 10 \
    "https://hc-ping.com/TU-UUID/fail" > /dev/null 2>&1 || true

Plan gratuito: 20 checks. Suficiente para 4 servidores con monitorización daily + weekly.

Script de verificación de frescura del backup:

#!/bin/bash
BACKUP_DIR="/mnt/nas/backups"
MAX_AGE_HOURS=26

find "$BACKUP_DIR" -name "*.log" -newer \
    <(date -d "$MAX_AGE_HOURS hours ago") > /tmp/recent_backups 2>/dev/null

if [ ! -s /tmp/recent_backups ]; then
    echo "ALERTA: Ningún backup reciente en $(hostname)" \
    | mail -s "[BACKUP] Backup demasiado antiguo!" admin@midominio.com
fi

Prometheus + Grafana para homelabs avanzados:

Para entornos con múltiples servidores, el node_exporter de Prometheus expone métricas del sistema de archivos. Una regla de Alertmanager puede disparar una notificación Slack si el timestamp del último backup supera un umbral:

# prometheus/rules/backup.yml
groups:
  - name: backup_freshness
    rules:
      - alert: BackupStaleness
        expr: |
          (time() - node_filesystem_file_content_mtime_seconds{
            mountpoint="/mnt/nas",
            path="/data/backups/vps-main"
          }) > 90000
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "Backup obsoleto en {{ $labels.instance }}"
          description: "Último backup hace {{ $value | humanizeDuration }}"

Este dashboard Grafana corre en mi Raspberry Pi 4 (8 GB RAM), que también sirve como monitorización central para mis 4 servidores. Coste total de la infraestructura de monitorización: 0 € (Prometheus + Grafana open source, alojados en el Pi).


La automatización con cron + rsync es la capa técnica que hace viable la estrategia de backup 3-2-1 a escala. Para entender cómo encaja en una arquitectura de backup completa, consulta la guía estrategia backup 3-2-1. Para máquinas Windows y Mac, la guía backup automático Windows y Mac cubre los equivalentes con interfaz gráfica.

Cuando la prevención falla y la recuperación de datos es necesaria, la guía recuperación de datos disco duro 2026 documenta las opciones DIY y profesionales, y la comparativa mejores software de recuperación de datos 2026 compara las herramientas del mercado. Para estimar el coste de una recuperación profesional, consulta nuestra guía de precios recuperación datos disco duro 2026.

★ Éditeur fondé en 2004 · ✓ Garantie 30 jours · Version gratuite jusqu'à 2 Go

Probar EaseUS Data Recovery Wizard30 jours satisfait ou remboursé