Academia Deltamontero · Documentación
Operación y mantenimiento

Respaldos

Qué se respalda, cómo se activa el respaldo diario en el servidor, cómo se vigila desde Moodle y cómo configurar la copia cifrada fuera del servidor.

El respaldo de la Academia lo hace el servidor, no Moodle: todos los días guarda la base de datos y los archivos subidos en un solo archivo verificado, y le avisa a Moodle cómo salió. Los scripts están en infrastructure/scripts/ y viajan dentro de la imagen de Moodle en /opt/delta-lms.

Qué hay que proteger

ParteDónde vive¿Está en el respaldo?
Base de datos (usuarios, módulos, avances, notas, certificados, reglas, configuración, registro de correos)MariaDB 11.4, volumen mariadb_dataSí (database.sql.gz)
Archivos subidos (PDF, imágenes, documentos)Volumen moodledataSí (moodledata.tar.gz, sin cachés ni sesiones)
Código y configuración de la plataformaRepositorio de GitHub (la imagen se construye desde ahí y los ajustes se aplican solos en cada despliegue)No hace falta: se reconstruye desde el repositorio
Claves y configuración del correo (SMTP), de la IA y de la base de datosVariables de entorno de CoolifyNo por defecto (--with-env las incluye en texto plano). Deben guardarse en un gestor de contraseñas
VideosYouTube y Cloudflare R2No: están fuera del servidor

Si se pierden los volúmenes mariadb_data y moodledata, se pierde la información aunque el código esté en GitHub. Por eso el respaldo cubre exactamente esos dos.

Los respaldos contienen datos personales (nombres, cédulas, correos, notas) y los hashes de las contraseñas. Los archivos se crean con permisos solo para el dueño (umask 077) y la carpeta del servidor con chmod 700. La copia fuera del servidor debe ir cifrada.

Estado del plan de respaldo

ElementoEstado
Script de respaldo (backup.sh): base + moodledata en un archivo, con sha256 y manifiestoHecho y probado
Script de restauración (restore.sh): mismo servidor, servidor nuevo con o sin Coolify, simulación --dry-runHecho y probado
Respaldo diario (backup-daily.sh): respaldo, verificación, copia externa y aviso a MoodleHecho y probado en local
Instalación en el servidor con un comando (setup-backup.sh): tarea diaria a las 2:00 de Ecuador, 14 días, registro y primer respaldoHecho y activo en producción desde el 2026-10-06
Aviso en Moodle: el super administrador ve si no hay respaldo, si falló, si tiene más de un día o si no hay copia externa; si falla, llega un correoHecho y probado en local
Copia diaria fuera del servidor (90 días)Hecho en el script. Pendiente: elegir y configurar el destino
Snapshot de Proxmox del contenedorPendiente (lo programa TI)
Variables de entorno guardadas aparte (gestor de contraseñas), incluida la clave del correoPendiente
Reloj del servidor sincronizado (NTP en el nodo Proxmox)Pendiente (TI): el 2026-10-06 estaba 16 minutos adelantado y sin sincronizar

Objetivo: perder como máximo un día de datos (RPO 24 h) y volver a estar en línea en unas 2 horas (RTO), contando la construcción de la imagen.

Formato del respaldo

delta-lms_backup_2026-09-28_024132.tar         ← archivo único
delta-lms_backup_2026-09-28_024132.tar.sha256  ← checksum (restore.sh lo exige)
  └─ delta-lms_backup_.../
       database.sql.gz     mariadb-dump --single-transaction --routines --triggers (utf8mb4)
       moodledata.tar.gz   sin cache/ localcache/ sessions/ temp/ trashdir/ (Moodle los regenera)
       manifest.txt        fecha, versión de Moodle, wwwroot, collation, nº de tablas y archivos
       SHA256SUMS          checksums de los archivos internos
       env.backup          solo con --with-env

El manifiesto guarda, entre otros: fecha, servidor, proyecto de Compose, contenedores, nombre y cotejamiento de la base, número de tablas, versión de Moodle, dirección de origen (source_wwwroot), número de entradas de moodledata, si se usó el modo de mantenimiento y si incluye las variables.

Activar el respaldo diario

El repositorio es privado, así que los scripts no se clonan en el servidor: viajan dentro de la imagen de Moodle (/opt/delta-lms) y se copian al servidor desde el contenedor. Requiere haber hecho un Redeploy con una versión que los incluya.

Abrir la terminal del servidor

En Coolify → Terminal → localhost (el servidor, no un contenedor), como root.

Copiar los scripts e instalar el respaldo

C=$(docker ps --filter label=com.docker.compose.service=moodle --format '{{.Names}}' | head -n1); mkdir -p /opt/delta-lms && docker cp "$C":/opt/delta-lms/. /opt/delta-lms/ && bash /opt/delta-lms/infrastructure/scripts/setup-backup.sh

La línea busca el contenedor de Moodle por su etiqueta, copia /opt/delta-lms al servidor y ejecuta setup-backup.sh.

Comprobar el primer respaldo

setup-backup.sh hace un primer respaldo en el momento. Si falla, termina con "El primer respaldo falló: revise /var/log/delta-lms-backup.log". Si sale bien, el estado aparece en Moodle (ver más abajo).

Para actualizar los scripts después de otro Redeploy, se vuelve a ejecutar la misma línea. Volver a ejecutar setup-backup.sh también actualiza la configuración.

Qué deja instalado setup-backup.sh

ArchivoContenido
/etc/delta-lms-backup.confConfiguración: BACKUP_DIR, BACKUP_KEEP, RCLONE_REMOTE, OFFSITE_KEEP_DAYS, BACKUP_HOUR
/etc/cron.d/delta-lms-backupTarea diaria que ejecuta backup-daily.sh como root y añade la salida al registro
/etc/logrotate.d/delta-lms-backupRotación semanal del registro, 8 semanas, comprimido
/var/log/delta-lms-backup.logRegistro de cada respaldo
/var/backups/delta-lmsCarpeta de respaldos (permisos 700)

Si el servicio cron del servidor no está activo, el script lo avisa:

systemctl enable --now cron

Opciones de setup-backup.sh

OpciónPara quéPor defecto
--remote REMOTODestino rclone ya configurado para la copia fuera del servidorSin copia externa
--dir RUTACarpeta de respaldos en el servidor/var/backups/delta-lms
--keep NRespaldos que se guardan en el servidor14
--offsite-days NDías que se guardan afuera90
--hour HHora del respaldo diario, en hora de Ecuador2
--no-runNo hacer el primer respaldo ahora—
--helpAyuda—

La hora: Ecuador frente a UTC

El servidor está en UTC. setup-backup.sh recibe la hora en hora de Ecuador (UTC-5, sin horario de verano) y la convierte sola a la hora del reloj del servidor, sea cual sea su zona. Con el valor por defecto, 2:00 de Ecuador queda como 7:00 UTC (0 7 * * * en /etc/cron.d/delta-lms-backup).

Qué hace el respaldo diario

backup-daily.sh (lo ejecuta el cron del servidor):

  1. Ejecuta backup.sh: base de datos + moodledata en un .tar con su .sha256, y conserva los últimos BACKUP_KEEP en el servidor.
  2. Verifica el sha256 del archivo nuevo.
  3. Si RCLONE_REMOTE está configurado, copia el respaldo fuera del servidor y borra allá las copias de más de OFFSITE_KEEP_DAYS días. Si no, registra el aviso "sin copia externa".
  4. Le avisa a Moodle cómo salió con local/deltamodules/cli/backup_status.php. Si falló, Moodle envía un correo a los super administradores y al correo de soporte, y el super administrador ve el aviso en su Área personal.

Una ejecución correcta termina en el registro con === Respaldo diario: correcto (<archivo>, <bytes> bytes) ===.

backup.sh paso a paso

PasoQué hace
[1/6]Detecta los contenedores (por etiqueta de servicio, o por nombre) y lee las credenciales del contenedor de MariaDB
[2/6]Modo de mantenimiento, solo con --maintenance (se desactiva siempre al terminar, incluso si falla)
[3/6]Volcado de la base con mariadb-dump (instantánea consistente con --single-transaction)
[4/6]Empaqueta moodledata sin cachés, sesiones ni temporales
[5/6]Genera el manifiesto, las sumas internas y el archivo final con su .sha256
[6/6]Retención: conserva los últimos N respaldos

Si un archivo cambia mientras se copia moodledata (el sitio está en uso), solo sale un aviso: la base sigue siendo consistente.

Respaldo manual

infrastructure/scripts/backup.sh                         # → <repo>/backups (en .gitignore)
infrastructure/scripts/backup.sh /var/backups/delta-lms  # otro destino
infrastructure/scripts/backup.sh --keep 30               # retención (defecto 14; 0 = no borrar)
infrastructure/scripts/backup.sh --maintenance           # modo mantenimiento durante la copia

En el servidor, con los scripts ya copiados:

/opt/delta-lms/infrastructure/scripts/backup.sh /var/backups/delta-lms

--with-env agrega el .env al respaldo en texto plano, con todas las contraseñas. En Coolify no hay .env en el servidor (las variables viven en su interfaz), así que esta opción no aplica allí.

Códigos de salida

Códigobackup.shrestore.sh
0CorrectoCorrecto
1Error generalError general o cancelado
2Uso incorrectoUso incorrecto, o sin terminal y sin --yes
3Contenedor o credenciales no encontradosContenedor o credenciales
4Fallo del volcado de la baseRespaldo inválido o sha256 distinto
5Fallo de moodledataFallo en la base de datos
6Fallo al empaquetar o verificarFallo en moodledata
7—Fallo al arrancar los servicios

La página Respaldos de la Academia

En Moodle: Administración del sitio → Cursos → Copias de seguridad → Respaldos de la Academia. Es de solo lectura y muestra:

DatoDetalle
EstadoAl día (el último respaldo correcto tiene menos de 36 horas), Atrasado (el último correcto tiene más de 36 horas), Falló (con el motivo) o Sin respaldo
Último respaldo correctoFecha y hora
TamañoDel último archivo
Cuándo se haceTodos los días a la hora configurada (hora de Ecuador)
Se guardan en el servidorDías de retención
Última copia fuera del servidorFecha, o "No configurada"

La Copia de seguridad programada de Moodle (copias .mbz por curso) no se usa y queda apagada en cada despliegue: duplicaría cada curso dentro de moodledata y haría crecer el respaldo real sin aportar nada. El respaldo del servidor ya incluye todos los módulos con sus personas y avances.

Copia fuera del servidor

Si el servidor (o el nodo Proxmox) se daña, los respaldos guardados en él se pierden con él. La copia externa está programada en el script pero falta elegir y configurar el destino.

DestinoA favorEn contra
Cloudflare R2 (recomendado)Ya se usa para los videos; 10 GB gratis y luego muy barato; sin costo por descargar; fuera de la oficinaLa cuenta debe ser de la empresa (hoy la de videos es personal)
NAS o servidor de archivos de la empresaLos datos no salen de la empresaSi está en la misma oficina, un incendio o robo afecta a ambos; el servidor debe poder llegar a él
OneDrive / SharePoint de Office 365La empresa ya tiene Office 365La conexión de rclone depende de un token que vence y hay que renovarlo

La copia va cifrada con un remoto crypt de rclone. La clave de cifrado se guarda en el gestor de contraseñas: sin ella, la copia no se puede abrir.

Configurar la copia en Cloudflare R2

Crear el bucket

En Cloudflare (cuenta de la empresa): R2 → crear el bucket delta-lms-backups → crear un token de API con permiso de lectura y escritura solo en ese bucket.

Instalar rclone y crear los remotos

En el servidor, instalar rclone (apt install rclone) y crear los dos remotos. Las claves van solo en el servidor (/root/.config/rclone/rclone.conf), nunca en el repositorio:

rclone config create r2 s3 provider=Cloudflare access_key_id=<ID> secret_access_key=<CLAVE> \
    endpoint=https://<ID_DE_CUENTA>.r2.cloudflarestorage.com
rclone config create r2crypt crypt remote=r2:delta-lms-backups password=<CLAVE_DE_CIFRADO> --obscure

Activar la copia diaria

bash /opt/delta-lms/infrastructure/scripts/setup-backup.sh --remote r2crypt:

setup-backup.sh comprueba que rclone puede escribir en el destino antes de guardar la configuración.

Para traer una copia externa al servidor:

rclone copy r2crypt:<archivo>.tar . && rclone copy r2crypt:<archivo>.tar.sha256 .

Respaldo manual en Coolify sin setup-backup.sh

La forma recomendada es setup-backup.sh. A mano, sin .env (las credenciales se leen de los contenedores):

C=$(docker ps --filter label=com.docker.compose.service=moodle --format '{{.Names}}' | head -n1)
sudo mkdir -p /opt/delta-lms && sudo docker cp "$C":/opt/delta-lms/. /opt/delta-lms/
sudo mkdir -p /var/backups/delta-lms
sudo crontab -e
# 0 2 * * * /opt/delta-lms/infrastructure/scripts/backup.sh /var/backups/delta-lms >> /var/log/delta-lms-backup.log 2>&1

En esta variante manual la hora del crontab es la del reloj del servidor (UTC), no la de Ecuador, y Moodle no recibe el estado del respaldo (no hay avisos ni correo de fallo). Por eso se prefiere setup-backup.sh.

Coolify puede renombrar contenedores y volúmenes (por ejemplo moodle-<uuid> o <uuid>_moodledata). Los scripts los encuentran por etiqueta y por los montajes del contenedor, así que no hay que configurar nombres. Si hay más de un despliegue en el mismo servidor, defina COMPOSE_PROJECT_NAME=<uuid>.

Probar un respaldo sin tocar la base en uso

Para comprobar que un archivo se puede cargar, sin tocar la instalación en uso, se carga en una base MariaDB temporal:

F=backups/delta-lms_backup_X.tar; N=$(basename "$F" .tar)
export MARIADB_ROOT_PASSWORD=$(openssl rand -hex 16) MYSQL_PWD=$MARIADB_ROOT_PASSWORD
docker run -d --name bk-test-db -e MARIADB_ROOT_PASSWORD mariadb:11.4
# esperar ~20 s
docker exec -e MYSQL_PWD bk-test-db mariadb -uroot -e "CREATE DATABASE t CHARACTER SET utf8mb4"
tar -xOf "$F" "$N/database.sql.gz" | gzip -dc | docker exec -i -e MYSQL_PWD bk-test-db mariadb -uroot t
docker exec -e MYSQL_PWD bk-test-db mariadb -uroot -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='t'; SELECT COUNT(*) FROM t.mdl_user"
docker rm -f bk-test-db

La prueba completa (restaurar todo en una instalación aparte) está en Recuperación y los resultados de las pruebas hechas, en Registro de pruebas.

En esta página