
Un respaldo que vive en el mismo servidor no sirve si el servidor se pierde, se daña o es comprometido. La solución es tener una copia fuera, y una cuenta gratuita de Google Drive (15 GB) alcanza para varias tiendas pequeñas.
Usaremos rclone para subir los respaldos cifrados: en Drive solo se ven archivos ilegibles, y solo se abren con tus claves. Y subir-respaldos.sh lo deja automático: sube, verifica, ordena y avisa si algo falla.
Antes de empezar
- Necesitas un VPS o servidor dedicado con cPanel/WHM y acceso root (desde WHM → Terminal basta).
- Los scripts se probaron en AlmaLinux 10 con cPanel 138, PHP 8.3 de EasyApache (
/opt/cpanel/ea-php83) y WP-CLI instalado en/usr/local/bin/wp. Si tu servidor usa otra versión de PHP, cambia la rutaea-php83en el script. - Pruébalos primero en un horario tranquilo y lee lo que hace cada uno antes de ejecutarlo. Se comparten tal cual, sin garantía: adáptalos a tu servidor.
Necesitas además una cuenta de Google dedicada solo a los respaldos (recomendado) y respaldos de cPanel funcionando (ver la entrada anterior).
1. Instalar rclone
curl -s https://rclone.org/install.sh | bash
rclone version | head -1
2. Crear tu propio acceso en Google Cloud
rclone trae un acceso compartido a Google Drive, pero tiene límites y Google lo restringe cada vez más. Lo recomendable es crear uno propio (es gratis):
- Entra a https://console.cloud.google.com con la cuenta de los respaldos y crea un proyecto nuevo.
- En APIs y servicios → Biblioteca, habilita Google Drive API.
- En Google Auth Platform (pantalla de consentimiento): tipo Externo, completa el nombre de la app, tu correo, la página principal y la de privacidad de tu sitio, y el dominio autorizado. Sin estos datos, el botón para publicar la app queda gris.
- En Acceso a datos, agrega el permiso
.../auth/drive.file: rclone solo podrá ver y modificar los archivos que él mismo cree, no el resto de tu Drive. - En Público, pulsa Publicar app. Si la dejas «en prueba», la autorización vence cada 7 días.
- En Clientes, crea un cliente OAuth de tipo App de escritorio y guarda su ID y su secreto en tu gestor de contraseñas.
El ID y el secreto del cliente, el código de autorización y las claves de cifrado son credenciales: no los pegues en chats, correos ni foros.
3. Configurar rclone en el servidor
Corre rclone config y crea un remoto nuevo llamado gdrive, de tipo drive. Pega tu client_id y client_secret, elige el permiso drive.file y responde n a «Use web browser». rclone te mostrará un comando rclone authorize "drive" "...": córrelo en un computador con navegador y rclone instalado, entra con la cuenta de los respaldos y pega en el servidor el código que te devuelve.
Después crea el remoto cifrado sobre una carpeta de Drive. Inventa dos claves largas y guárdalas en tu gestor de contraseñas: sin ellas, los respaldos no se pueden abrir.
rclone config create gdrive-cifrado crypt remote=gdrive:respaldos-servidor \
filename_encryption=off directory_name_encryption=false \
password=$(rclone obscure "CLAVE_LARGA_1") password2=$(rclone obscure "CLAVE_LARGA_2")
rclone lsd gdrive: && echo "Conexion con Drive OK"
Con filename_encryption=off los nombres de carpetas y archivos quedan visibles (para ubicar las fechas), pero el contenido va cifrado.
4. El script: subir-respaldos.sh
- Sube el último respaldo diario de las cuentas que elijas a
diarios/FECHAy el último mensual amensual/FECHA. - Después de subir cada archivo, lo compara con el original (
rclone cryptcheck) y deja unCOMPLETO.txtcomo marca de subida terminada. - Deja en Drive los últimos 3 diarios y 1 mensual, con un tope de espacio (
TOPE_GB). Si se pasa, borra el diario más antiguo, nunca el último. - Si algo falla (sin conexión, autorización vencida o archivo que no coincide), te avisa por correo.
- Tiene dos modos más:
estado(qué hay en Drive) ybajar FECHA CUENTA(descarga y descifra un respaldo).
Instalación
Entra a WHM → Terminal (como root) y pega este bloque completo de una vez. Crea el archivo /root/subir-respaldos.sh, le da permisos y comprueba que quedó bien escrito:
cat > /root/subir-respaldos.sh <<'EOF'
#!/bin/bash
# Copia cifrada de los respaldos de las tiendas a Google Drive (rclone, remoto "gdrive-cifrado").
# Guarda en Drive los ultimos 3 respaldos diarios y el mensual mas reciente, con un tope de espacio.
# Uso: /root/subir-respaldos.sh sube lo nuevo y ordena Drive (lo corre el cron cada dia)
# /root/subir-respaldos.sh estado muestra que hay en Drive
# /root/subir-respaldos.sh bajar FECHA CUENTA descarga y descifra un respaldo a /root/restaurar-drive
CUENTAS="cuenta1 cuenta2 cuenta3" # usuarios de cPanel que se copian a Drive
MANTENER=3
TOPE_GB=13
REMOTO="gdrive-cifrado:"
DIR=${BACKUP_DIR:-/backup}
LOG=${LOG:-/var/log/subir-respaldos.log}
AVISO=root
TOPE=$((TOPE_GB*1024*1024*1024))
h(){ numfmt --to=iec --suffix=B "$1"; }
log(){ echo "$(date '+%F %T') $1" | tee -a "$LOG"; }
fallo(){ log "ERROR: $1"
printf "To: %s\nSubject: [$(hostname -s)] FALLO la copia de respaldos a Google Drive\n\n%s\n\nUltimas lineas del registro (%s):\n%s\n" \
"$AVISO" "$1" "$LOG" "$(tail -15 "$LOG")" | /usr/sbin/sendmail -t 2>/dev/null; exit 1; }
listar(){ rclone lsf --dirs-only "$REMOTO$1" 2>/dev/null | tr -d / | grep -E '^20[0-9]{2}-[0-9]{2}-[0-9]{2}$' | sort; }
case "$1" in
estado)
echo "=== Respaldos en Google Drive"; for t in diarios mensual; do for f in $(listar $t/); do
echo "$t/$f $(rclone size --json "$REMOTO$t/$f" 2>/dev/null | grep -oE '"bytes":[0-9]+' | cut -d: -f2 | xargs -I{} numfmt --to=iec --suffix=B {}) $(rclone cat "$REMOTO$t/$f/COMPLETO.txt" 2>/dev/null | head -1)"; done; done
echo -n "=== Total: "; rclone size "$REMOTO" 2>/dev/null | grep -i "total size"; exit 0 ;;
bajar)
[ -n "$2" ] && [ -n "$3" ] || { echo "Uso: $0 bajar FECHA CUENTA (ej: $0 bajar 2026-10-18 cuenta1)"; exit 1; }
for t in diarios mensual; do rclone lsf "$REMOTO$t/$2/$3.tar.gz" >/dev/null 2>&1 && SRC="$REMOTO$t/$2/$3.tar.gz"; done
[ -n "$SRC" ] || { echo "No existe $2/$3.tar.gz en Drive. Ver: $0 estado"; exit 1; }
mkdir -p /root/restaurar-drive && rclone copy -P "$SRC" /root/restaurar-drive/$2/ && echo "Descargado y descifrado en /root/restaurar-drive/$2/$3.tar.gz"; exit 0 ;;
esac
pgrep -f "cpbackup|pkgacct|bin/backup" >/dev/null 2>&1 && { log "Hay un respaldo en curso: se intentara mas tarde"; exit 0; }
rclone lsd "$REMOTO" >/dev/null 2>&1 || rclone mkdir "$REMOTO" >/dev/null 2>&1 || fallo "No hay conexion con Google Drive (sin internet o autorizacion vencida: repetir 'rclone config reconnect gdrive:')"
subir(){ # $1 carpeta local del respaldo, $2 destino (diarios|mensual)
local D="$1" F; F=$(basename "$D"); local DEST="$REMOTO$2/$F"
rclone lsf "$DEST/COMPLETO.txt" >/dev/null 2>&1 && return 0
log "Subiendo $2/$F"
local n=0 tot=0
for U in $CUENTAS; do
local A="$D/accounts/$U.tar.gz"
[ -f "$A" ] || { log " (no existe $U.tar.gz en $F, se omite)"; continue; }
rclone copy --retries 5 --low-level-retries 20 "$A" "$DEST/" >>"$LOG" 2>&1 || fallo "No se pudo subir $U.tar.gz de $F"
rclone cryptcheck "$D/accounts" "$DEST" --include "$U.tar.gz" >>"$LOG" 2>&1 || fallo "La copia de $U.tar.gz de $F no coincide con el original"
n=$((n+1)); tot=$((tot+$(stat -c %s "$A")))
done
[ $n -gt 0 ] || fallo "El respaldo $F no tiene ninguna de las cuentas: $CUENTAS"
echo "Completo: $n cuentas, $(h $tot), subido $(date '+%F %T')" | rclone rcat "$DEST/COMPLETO.txt" || fallo "No se pudo cerrar $F"
log " listo: $n cuentas, $(h $tot)"
}
ULT=$(ls -1d "$DIR"/20??-??-?? 2>/dev/null | sort | tail -1)
[ -n "$ULT" ] || fallo "No hay respaldos diarios en $DIR"
subir "$ULT" diarios
MEN=$(ls -1d "$DIR"/monthly/20??-??-?? 2>/dev/null | sort | tail -1)
[ -n "$MEN" ] && subir "$MEN" mensual
# limpiar: dejar los ultimos $MANTENER diarios completos y 1 mensual; borrar subidas incompletas antiguas
for F in $(listar diarios/); do rclone lsf "${REMOTO}diarios/$F/COMPLETO.txt" >/dev/null 2>&1 || { [ "$F" != "$(basename "$ULT")" ] && rclone purge "${REMOTO}diarios/$F" && log "Borrada subida incompleta diarios/$F"; }; done
L=($(listar diarios/)); while [ ${#L[@]} -gt $MANTENER ]; do rclone purge "${REMOTO}diarios/${L[0]}" && log "Borrado de Drive: diarios/${L[0]}"; L=("${L[@]:1}"); done
M=($(listar mensual/)); while [ ${#M[@]} -gt 1 ]; do rclone purge "${REMOTO}mensual/${M[0]}" && log "Borrado de Drive: mensual/${M[0]}"; M=("${M[@]:1}"); done
# tope de espacio: si se pasa, borrar el diario mas antiguo (nunca el ultimo)
T=$(rclone size --json "$REMOTO" 2>/dev/null | grep -oE '"bytes":[0-9]+' | cut -d: -f2)
L=($(listar diarios/))
while [ "${T:-0}" -gt "$TOPE" ] && [ ${#L[@]} -gt 1 ]; do
rclone purge "${REMOTO}diarios/${L[0]}" && log "Borrado por tope de ${TOPE_GB}GB: diarios/${L[0]}"; L=("${L[@]:1}")
T=$(rclone size --json "$REMOTO" 2>/dev/null | grep -oE '"bytes":[0-9]+' | cut -d: -f2)
done
log "En Drive: ${#L[@]} diarios + $(listar mensual/ | wc -l) mensual, total $(h ${T:-0}) de ${TOPE_GB}GB"
EOF
chmod 700 /root/subir-respaldos.sh && bash -n /root/subir-respaldos.sh && echo SCRIPT_OK
Si al final aparece SCRIPT_OK, el script está listo. Si no aparece, el bloque se pegó incompleto o con comillas cambiadas: vuelve a pegarlo.
Ajusta al comienzo del script CUENTAS (los usuarios de cPanel que quieres copiar), MANTENER, TOPE_GB (deja margen bajo los 15 GB gratis) y AVISO (a quién avisar; root llega al correo de contacto del servidor). Pruébalo a mano y luego prográmalo:
/root/subir-respaldos.sh
/root/subir-respaldos.sh estado
(crontab -l 2>/dev/null; echo "30 9 * * * /root/subir-respaldos.sh >/dev/null 2>&1") | crontab - && crontab -l | grep subir
La hora del cron es la del servidor (normalmente UTC): elige una hora en que los respaldos de cPanel ya hayan terminado.
5. Prueba que puedes recuperar
Un respaldo que nunca se probó no es un respaldo. Descarga uno y compáralo con el original:
/root/subir-respaldos.sh bajar AAAA-MM-DD cuenta1
cmp /root/restaurar-drive/AAAA-MM-DD/cuenta1.tar.gz /backup/AAAA-MM-DD/accounts/cuenta1.tar.gz && echo "RESPALDO RECUPERABLE OK"
rm -rf /root/restaurar-drive
Si el servidor se pierde
Desde cualquier equipo con Linux: instala rclone, crea gdrive con tu mismo cliente de Google y crea gdrive-cifrado con las mismas dos claves. Luego descarga con rclone copy -P gdrive-cifrado:diarios/AAAA-MM-DD ./recuperado/ y restaura los .tar.gz en el servidor nuevo desde WHM → Transfers → Restore a Full Backup/cpmove File.
Renovar el secreto o la autorización
- En Google Cloud → Clientes → tu cliente → Agregar secreto.
- En el servidor:
rclone config update gdrive client_secret NUEVO_SECRETO. - Reautoriza:
rclone config reconnect gdrive:(Refresh token: y; Use web browser: n) y completa elrclone authorizedesde tu computador. - Comprueba:
rclone lsd gdrive: && /root/subir-respaldos.sh estado. - Recién entonces inhabilita y borra el secreto anterior en Google Cloud.





