Introducción
La mayoría de los proveedores de VPS ofrecen backups — pero cuando lees la letra pequeña, resulta que son snapshots que corren una vez por semana, que no siempre se pueden restaurar de forma granular, y que en algunos planes hay que pagar extra por ellos. No es que no existan: es que no puedes confiar en ellos como tu única copia de seguridad.
El problema real no es la falta de herramientas. Es que muchos administradores configuran un backup, ven que el cron corre sin errores, y asumen que todo está funcionando. Tres meses después, cuando necesitan recuperar un archivo, descubren que el proceso falló silenciosamente desde la semana dos porque la autenticación expiró, se llenó el disco destino, o simplemente el script tenía un bug que nadie notó. Un backup que no has probado restaurar no es un backup: es una esperanza.
Esta guía resuelve eso. Al final tendrás backups automáticos diarios de tu VPS a Google Drive usando rclone, con verificación de integridad y un proceso documentado para restaurar archivos individuales o directorios completos. También cubre bases de datos MySQL y la rotación automática de backups antiguos.
"La pregunta no es si tu servidor va a fallar, sino cuándo."
Qué vas a configurar: backup automático diario a Google Drive con verificación de integridad, rotación de copias antiguas y alertas si algo falla. Todo usando herramientas open source y sin costo adicional más allá del almacenamiento en Drive.
Requisitos previos
VPS con Ubuntu 20.04 o superior, cuenta de Google con espacio disponible en Drive (mínimo el doble del tamaño de tu servidor), y acceso SSH con privilegios de root o sudo.
Instalar rclone
Hay dos formas de instalar rclone en Ubuntu. La primera es a través de apt, que es conveniente pero instala una versión antigua. La segunda es el instalador oficial, que siempre instala la última versión estable.
Método 1: apt (no recomendado)
La versión en los repositorios de Ubuntu suele estar varios releases por detrás. En este momento, instalar desde apt en Ubuntu 22.04 te da rclone v1.53 cuando la versión actual es v1.67+. Esto importa porque la API de Google Drive ha cambiado y las versiones antiguas pueden tener problemas de autenticación.
# Método 1: apt (versión antigua, no recomendada)
sudo apt update
sudo apt install rclone
rclone version
Método 2: instalador oficial (recomendado)
El instalador oficial descarga siempre la última versión estable directamente desde rclone.org. Es un script que detecta tu arquitectura automáticamente y pone el binario en /usr/bin/rclone.
# Método 2: instalador oficial (recomendado)
curl https://rclone.org/install.sh | sudo bash
rclone version
La razón para preferir este método es la compatibilidad con la API de Google Drive. Google actualiza sus OAuth scopes y tokens periódicamente, y las versiones antiguas de rclone pueden dejar de funcionar sin previo aviso. Con el instalador oficial puedes actualizar fácilmente con el mismo comando cuando salga una nueva versión.
Configurar rclone con Google Drive
La configuración de rclone con Google Drive requiere dos pasos: crear una aplicación OAuth en Google Cloud Console y luego ejecutar rclone config para autenticar.
Crear un OAuth Client ID propio
Por defecto, rclone usa credenciales OAuth compartidas entre todos sus usuarios. Esto significa que cuando miles de personas usan el mismo Client ID, Google aplica rate limiting colectivo. Si alguien en el otro lado del mundo está haciendo un backup masivo con el mismo Client ID, tu backup puede fallar por rate limiting aunque tú no hayas hecho nada.
La solución es crear tu propio OAuth Client ID. Tarda 5 minutos y elimina completamente ese problema:
- Ve a console.cloud.google.com e inicia sesión con tu cuenta de Google
- Crea un proyecto nuevo (o usa uno existente) — el nombre no importa, puede ser "rclone-backups"
- En el menú lateral, ve a APIs & Services → Library y busca "Google Drive API". Habilítala.
- Ve a APIs & Services → Credentials y haz clic en Create Credentials → OAuth 2.0 Client ID
- Si te pide configurar el OAuth consent screen primero, selecciona "External", pon cualquier nombre de aplicación y tu email. En "Test users" agrega tu propia cuenta de Google.
- Para el tipo de aplicación selecciona Desktop app
- Anota el Client ID y el Client Secret que aparecen — los necesitarás en el siguiente paso
Nota
Las credenciales OAuth de tipo "Desktop app" son las correctas para rclone en un servidor. No uses "Web application" porque requiere redirect URIs específicas que complican el proceso de autenticación en CLI.
Ejecutar rclone config
Con el Client ID y Client Secret en mano, ejecuta el asistente de configuración de rclone. Este proceso solo necesitas hacerlo una vez — la configuración queda guardada en /root/.config/rclone/rclone.conf.
rclone config
El asistente te irá preguntando paso a paso. Estas son las respuestas que necesitas dar:
n) New remote
name> gdrive
Storage> drive (o el número correspondiente a Google Drive)
client_id> [tu-client-id]
client_secret> [tu-client-secret]
scope> 1 (full access — drive)
root_folder_id> (dejar vacío, Enter)
service_account_file> (dejar vacío, Enter)
Edit advanced config? n
Use auto config? n
Cuando responde n a "Use auto config", rclone genera una URL para que la abras en un navegador. Como el servidor no tiene navegador, copia esa URL, ábrela en tu computadora local, autoriza el acceso con tu cuenta de Google y pega el código de verificación de vuelta en la terminal del servidor.
Importante
Ejecuta rclone config como root (con sudo su primero o directamente como root). Si lo ejecutas como tu usuario y luego el script corre como root desde cron, no encontrará la configuración porque cada usuario tiene su propio directorio .config/rclone/.
Verificar la conexión
Antes de automatizar nada, confirma que la conexión funciona correctamente:
# Listar contenido de Google Drive
rclone ls gdrive:
# Crear carpeta de prueba
rclone mkdir gdrive:backups/test
# Subir archivo de prueba
echo "test backup $(date)" > /tmp/test-backup.txt
rclone copy /tmp/test-backup.txt gdrive:backups/test/
rclone ls gdrive:backups/test/
Si el último comando muestra el archivo, la conexión está funcionando correctamente. Elimina la carpeta de prueba cuando termines:
rclone purge gdrive:backups/test
El script de backup
Un simple rclone sync en cron es suficiente para empezar, pero no para confiar en él. El script que viene a continuación agrega: lock file para evitar backups paralelos, logging detallado con timestamps, backup automático de bases de datos MySQL si están presentes, rotación de archivos antiguos y reporte del tamaño total en Drive.
Script completo
#!/bin/bash
# ============================================
# backup-gdrive.sh — Backup automático a Google Drive
# Autor: Jose M. | fixdop.com
# Uso: bash /usr/local/bin/backup-gdrive.sh
# ============================================
set -euo pipefail
# ===== CONFIGURACIÓN (ajustar según tu servidor) =====
RCLONE_REMOTE="gdrive"
BACKUP_DEST="${RCLONE_REMOTE}:backups/$(hostname)"
BACKUP_SOURCES=(
"/var/www"
"/etc/nginx"
"/etc/letsencrypt"
"/home"
"/root"
)
LOG_DIR="/var/log/backups"
LOCK_FILE="/tmp/rclone-backup.lock"
RETENTION_DAYS=30
# ===== FUNCIONES =====
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }
cleanup() {
log "Limpiando lock file..."
rm -f "$LOCK_FILE"
}
# ===== VERIFICAR LOCK =====
if [[ -f "$LOCK_FILE" ]]; then
log "ERROR: Backup ya en ejecución (PID: $(cat $LOCK_FILE)). Saliendo."
exit 1
fi
echo $$ > "$LOCK_FILE"
trap cleanup EXIT
# ===== PREPARAR LOG =====
mkdir -p "$LOG_DIR"
LOG_FILE="${LOG_DIR}/backup-$(date +%Y-%m-%d).log"
exec > >(tee -a "$LOG_FILE") 2>&1
log "====== INICIO DE BACKUP ======"
log "Servidor: $(hostname)"
log "Destino: ${BACKUP_DEST}"
# ===== VERIFICAR RCLONE =====
if ! command -v rclone &> /dev/null; then
log "ERROR: rclone no está instalado"
exit 1
fi
# ===== SINCRONIZAR DIRECTORIOS =====
ERRORS=0
for SOURCE in "${BACKUP_SOURCES[@]}"; do
if [[ ! -d "$SOURCE" ]]; then
log "AVISO: $SOURCE no existe, saltando"
continue
fi
DEST="${BACKUP_DEST}${SOURCE}"
log "Sincronizando: $SOURCE → $DEST"
if rclone sync "$SOURCE" "$DEST" \
--transfers=4 \
--checkers=8 \
--tpslimit=8 \
--retries=3 \
--low-level-retries=10 \
--stats=60s \
--log-level WARNING \
--exclude="*.log" \
--exclude="*.tmp" \
--exclude=".git/**"; then
log "OK: $SOURCE completado"
else
log "ERROR: Falló la sincronización de $SOURCE"
ERRORS=$((ERRORS + 1))
fi
done
# ===== BACKUP DE BASES DE DATOS =====
if command -v mysqldump &> /dev/null && [[ -f /etc/mysql/debian.cnf ]]; then
log "Haciendo backup de bases de datos MySQL..."
DB_BACKUP_DIR="/tmp/db-backups"
mkdir -p "$DB_BACKUP_DIR"
mysql --defaults-file=/etc/mysql/debian.cnf -e "SHOW DATABASES;" 2>/dev/null | \
grep -vE "^(Database|information_schema|performance_schema|sys)$" | \
while read -r DB; do
log " Dumping DB: $DB"
mysqldump --defaults-file=/etc/mysql/debian.cnf \
--single-transaction --quick --lock-tables=false \
"$DB" | gzip > "${DB_BACKUP_DIR}/${DB}-$(date +%Y%m%d).sql.gz"
done
rclone sync "$DB_BACKUP_DIR" "${BACKUP_DEST}/databases/" \
--transfers=2 --retries=3
rm -rf "$DB_BACKUP_DIR"
log "OK: Bases de datos completadas"
fi
# ===== LIMPIAR BACKUPS ANTIGUOS =====
log "Eliminando backups con más de ${RETENTION_DAYS} días..."
rclone delete "${BACKUP_DEST}" \
--min-age "${RETENTION_DAYS}d" \
--log-level WARNING 2>/dev/null || true
# ===== RESUMEN =====
BACKUP_SIZE=$(rclone size "${BACKUP_DEST}" --json 2>/dev/null | \
python3 -c "import sys,json; d=json.load(sys.stdin); print(f\"{d['bytes']/1024/1024:.1f} MB\")" 2>/dev/null || echo "N/A")
log "====== FIN DE BACKUP ======"
log "Errores: ${ERRORS}"
log "Tamaño total en Drive: ${BACKUP_SIZE}"
if [[ $ERRORS -gt 0 ]]; then
log "ADVERTENCIA: Backup completado con ${ERRORS} errores"
exit 1
fi
log "Backup completado exitosamente"
exit 0
Instalar el script
Copia el script al servidor, dale permisos de ejecución y pruébalo manualmente antes de configurar el cron:
# Copiar el script al servidor
sudo nano /usr/local/bin/backup-gdrive.sh
# (pegar el contenido del script de arriba)
# Dar permisos de ejecución
sudo chmod +x /usr/local/bin/backup-gdrive.sh
# Prueba manual antes de automatizar
sudo bash /usr/local/bin/backup-gdrive.sh
Observa la salida. Cada directorio debería mostrar una línea "OK:" al terminar. Si hay errores, el script los reporta con el directorio exacto que falló y una descripción del problema. No sigas a la sección de automatización hasta que la prueba manual pase sin errores.
Automatizar con cron
Una vez que el script funciona manualmente, es hora de automatizarlo. Tienes dos opciones: cron (simple y universal) o systemd timers (más robusto, mejor logging).
Configurar crontab
La forma más directa: agregar una línea al crontab de root para que el script corra todos los días a las 3 AM.
# Editar crontab del root
sudo crontab -e
# Agregar esta línea (backup diario a las 3:00 AM)
0 3 * * * /usr/local/bin/backup-gdrive.sh >> /var/log/backups/cron.log 2>&1
El horario de las 3 AM es deliberado: es cuando el tráfico en la mayoría de servidores web está en su punto más bajo, y la API de Google Drive tiene menos carga. Ajusta la hora según tu zona horaria y los patrones de uso de tu servidor.
Alternativa con systemd timer
Los systemd timers tienen ventajas sobre cron: si el servidor estaba apagado a las 3 AM, el timer con Persistent=true ejecuta el backup al siguiente arranque. Además, los logs quedan en el journal de systemd junto con los demás servicios, lo que facilita el diagnóstico.
Crea primero el archivo de servicio:
# /etc/systemd/system/rclone-backup.service
[Unit]
Description=Backup a Google Drive con rclone
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-gdrive.sh
StandardOutput=journal
StandardError=journal
Luego el timer que lo activa:
# /etc/systemd/system/rclone-backup.timer
[Unit]
Description=Ejecutar backup diario a las 3 AM
Requires=rclone-backup.service
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
Activa y habilita el timer:
sudo systemctl daemon-reload
sudo systemctl enable --now rclone-backup.timer
sudo systemctl list-timers rclone-backup.timer
El último comando muestra cuándo correrá el siguiente backup. Si ves la línea con la fecha, el timer está activo.
Verificar que los backups funcionan
Esta es la sección más importante de toda la guía. La mayoría de la gente la salta — y es exactamente por eso que los backups fallan cuando más se necesitan. Configura una verificación periódica, no solo confíes en que "el cron no da error".
Verificar que los archivos llegaron a Drive
Después del primer backup automático, confirma que los archivos realmente están en Google Drive y tienen el tamaño esperado:
# Ver tamaño total del backup
rclone size gdrive:backups/$(hostname)/
# Listar archivos recientes
rclone ls gdrive:backups/$(hostname)/var/www/ | head -20
# Comparar con el directorio local
du -sh /var/www/
El tamaño reportado por rclone size debería ser comparable (no idéntico — rclone excluye *.log y *.tmp) al tamaño local reportado por du. Una diferencia grande indica que algo no se sincronizó correctamente.
Test de restauración
Una vez por semana (o al menos una vez por mes), descarga un archivo del backup y compara su checksum con el archivo local. Si son idénticos, el backup es íntegro. Si difieren, hay un problema.
# Descargar un archivo específico y comparar checksum
ARCHIVO="gdrive:backups/$(hostname)/etc/nginx/nginx.conf"
# Bajar copia del backup
rclone copy "$ARCHIVO" /tmp/restauracion-test/
# Comparar con el archivo local (deben ser idénticos)
diff /etc/nginx/nginx.conf /tmp/restauracion-test/nginx.conf && \
echo "✓ Backup íntegro" || echo "✗ DIFERENCIAS ENCONTRADAS"
# Limpiar
rm -rf /tmp/restauracion-test/
Para un test más completo, restaura un directorio entero a una ruta temporal y compara el contenido:
# Restaurar directorio completo a ruta temporal
rclone copy "gdrive:backups/$(hostname)/etc/nginx" /tmp/nginx-restore-test/
# Comparar recursivamente
diff -r /etc/nginx/ /tmp/nginx-restore-test/ && \
echo "✓ Directorio íntegro" || echo "✗ Diferencias encontradas"
rm -rf /tmp/nginx-restore-test/
Monitoreo del log
El script guarda logs diarios en /var/log/backups/. Revísalos regularmente:
# Ver el último log de backup
tail -50 /var/log/backups/backup-$(date +%Y-%m-%d).log
# Ver si hubo errores en el último mes
grep -l "ERROR\|ADVERTENCIA" /var/log/backups/backup-*.log
# Ver tamaño de los logs
du -sh /var/log/backups/
Si el segundo comando devuelve nombres de archivo, significa que esos días el backup tuvo problemas. Abre esos logs y lee los mensajes de error para diagnosticar qué falló.
Buena práctica
Configura una alerta por email si el script termina con código de error distinto de cero. Puedes hacerlo con MAILTO=tu@email.com al principio del crontab, o con un script de notificación que envíe un webhook.
Errores comunes
Estos son los problemas que aparecen con más frecuencia al configurar rclone en producción.
Rate limiting de Google API
Si ves este error en los logs:
Error 403: userRateLimitExceeded
Hay dos causas posibles. La primera es que estás usando las credenciales OAuth compartidas de rclone (el Client ID por defecto). La solución es usar tu propio OAuth Client ID como se describe en la sección de configuración — esto aísla tu cuota de la de todos los demás usuarios de rclone.
La segunda causa es que estás haciendo demasiadas peticiones por segundo. La opción --tpslimit=8 del script limita a 8 transacciones por segundo. Si el error persiste, bájalo:
# Reducir transacciones por segundo si persiste el rate limiting
rclone sync /var/www gdrive:backups/www \
--tpslimit=5 \
--transfers=2
"couldn't find home directory"
Este error aparece cuando el script corre como root desde cron pero la configuración de rclone fue creada con otro usuario. Cada usuario tiene su propio directorio de configuración, y root no puede acceder al de otros usuarios automáticamente.
Verifica dónde está el archivo de configuración de rclone para root:
sudo rclone config file
# Debe mostrar: /root/.config/rclone/rclone.conf
Si el archivo no existe en esa ruta, ejecuta sudo rclone config (como root) para crear la configuración específica para root. La configuración que hiciste con tu usuario normal no se comparte automáticamente.
Archivos grandes que generan timeout
Si tienes archivos muy grandes (volcados de base de datos sin comprimir, archivos de video, imágenes de disco), el timeout por defecto puede causar que la transferencia se interrumpa:
# Para archivos grandes, aumentar timeout y reducir paralelismo
rclone sync /var/www gdrive:backups/www \
--transfers=1 \
--timeout=300s \
--retries=5
Reducir --transfers a 1 elimina el paralelismo pero hace que cada archivo tenga más tiempo disponible para completarse. Combínalo con --timeout=300s para dar hasta 5 minutos a cada operación de red.
Espacio insuficiente en Google Drive
Si el backup falla porque se llenó Google Drive, primero verifica cuánto espacio estás usando:
# Ver cuánto espacio estás usando en Drive
rclone about gdrive:
Si el espacio es el problema, agrega exclusiones en el array BACKUP_SOURCES del script para directorios que no necesitan backup, o añade más filtros --exclude en el comando rclone sync. Los candidatos habituales son node_modules, directorios de caché, logs rotativos y archivos temporales:
--exclude="node_modules/**"
--exclude="vendor/**"
--exclude="*.log"
--exclude="*.tmp"
--exclude="cache/**"
--exclude=".git/**"