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
| Parte | Dó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_data | Sí (database.sql.gz) |
| Archivos subidos (PDF, imágenes, documentos) | Volumen moodledata | Sí (moodledata.tar.gz, sin cachés ni sesiones) |
| Código y configuración de la plataforma | Repositorio 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 datos | Variables de entorno de Coolify | No por defecto (--with-env las incluye en texto plano). Deben guardarse en un gestor de contraseñas |
| Videos | YouTube y Cloudflare R2 | No: 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
| Elemento | Estado |
|---|---|
Script de respaldo (backup.sh): base + moodledata en un archivo, con sha256 y manifiesto | Hecho y probado |
Script de restauración (restore.sh): mismo servidor, servidor nuevo con o sin Coolify, simulación --dry-run | Hecho y probado |
Respaldo diario (backup-daily.sh): respaldo, verificación, copia externa y aviso a Moodle | Hecho 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 respaldo | Hecho 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 correo | Hecho 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 contenedor | Pendiente (lo programa TI) |
| Variables de entorno guardadas aparte (gestor de contraseñas), incluida la clave del correo | Pendiente |
| 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-envEl 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.shLa 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
| Archivo | Contenido |
|---|---|
/etc/delta-lms-backup.conf | Configuración: BACKUP_DIR, BACKUP_KEEP, RCLONE_REMOTE, OFFSITE_KEEP_DAYS, BACKUP_HOUR |
/etc/cron.d/delta-lms-backup | Tarea diaria que ejecuta backup-daily.sh como root y añade la salida al registro |
/etc/logrotate.d/delta-lms-backup | Rotación semanal del registro, 8 semanas, comprimido |
/var/log/delta-lms-backup.log | Registro de cada respaldo |
/var/backups/delta-lms | Carpeta de respaldos (permisos 700) |
Si el servicio cron del servidor no está activo, el script lo avisa:
systemctl enable --now cronOpciones de setup-backup.sh
| Opción | Para qué | Por defecto |
|---|---|---|
--remote REMOTO | Destino rclone ya configurado para la copia fuera del servidor | Sin copia externa |
--dir RUTA | Carpeta de respaldos en el servidor | /var/backups/delta-lms |
--keep N | Respaldos que se guardan en el servidor | 14 |
--offsite-days N | Días que se guardan afuera | 90 |
--hour H | Hora del respaldo diario, en hora de Ecuador | 2 |
--no-run | No hacer el primer respaldo ahora | — |
--help | Ayuda | — |
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):
- Ejecuta
backup.sh: base de datos +moodledataen un.tarcon su.sha256, y conserva los últimosBACKUP_KEEPen el servidor. - Verifica el sha256 del archivo nuevo.
- Si
RCLONE_REMOTEestá configurado, copia el respaldo fuera del servidor y borra allá las copias de más deOFFSITE_KEEP_DAYSdías. Si no, registra el aviso "sin copia externa". - 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
| Paso | Qué 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 copiaEn 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ódigo | backup.sh | restore.sh |
|---|---|---|
| 0 | Correcto | Correcto |
| 1 | Error general | Error general o cancelado |
| 2 | Uso incorrecto | Uso incorrecto, o sin terminal y sin --yes |
| 3 | Contenedor o credenciales no encontrados | Contenedor o credenciales |
| 4 | Fallo del volcado de la base | Respaldo inválido o sha256 distinto |
| 5 | Fallo de moodledata | Fallo en la base de datos |
| 6 | Fallo al empaquetar o verificar | Fallo 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:
| Dato | Detalle |
|---|---|
| Estado | Al 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 correcto | Fecha y hora |
| Tamaño | Del último archivo |
| Cuándo se hace | Todos los días a la hora configurada (hora de Ecuador) |
| Se guardan en el servidor | Días de retención |
| Última copia fuera del servidor | Fecha, 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.
| Destino | A favor | En contra |
|---|---|---|
| Cloudflare R2 (recomendado) | Ya se usa para los videos; 10 GB gratis y luego muy barato; sin costo por descargar; fuera de la oficina | La cuenta debe ser de la empresa (hoy la de videos es personal) |
| NAS o servidor de archivos de la empresa | Los datos no salen de la empresa | Si está en la misma oficina, un incendio o robo afecta a ambos; el servidor debe poder llegar a él |
| OneDrive / SharePoint de Office 365 | La empresa ya tiene Office 365 | La 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> --obscureActivar 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>&1En 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-dbLa prueba completa (restaurar todo en una instalación aparte) está en Recuperación y los resultados de las pruebas hechas, en Registro de pruebas.
Arranque y actualizaciones
Qué hace la plataforma en cada arranque, pasos críticos y cosméticos, cómo ver los errores de despliegue y cómo actualizar Moodle, los plugins y los parches.
Recuperación (DRP)
Procedimiento paso a paso para volver a levantar la Academia Deltamontero a partir del repositorio y un respaldo. Requisitos y vacíos actuales, mismo servidor, servidor nuevo con o sin Coolify, verificación, volver atrás, correo SMTP y mudanza al contenedor nuevo.