Academia Deltamontero · Documentación
Operación y mantenimiento

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.

Esta página es el manual de recuperación (DRP) para TI. Es el procedimiento para volver a poner en marcha la Academia a partir del repositorio (el código) y un respaldo hecho con backup.sh (los datos; ver Respaldos), ya sea en el mismo servidor o en uno nuevo. Está escrito para que lo siga alguien de TI sin conocer la plataforma. Fuente: docs/drp.md, sección 4.

Hoy una pérdida total del servidor no se puede recuperar

Hay dos vacíos abiertos:

  1. Los respaldos están solo en el mismo servidor (/var/backups/delta-lms). Si el servidor se pierde, los respaldos se pierden con él hasta que se configure la copia externa (Respaldos).
  2. Las variables de entorno están solo en Coolify (claves de la base de datos, del correo y de la IA, y la dirección del sitio), y Coolify vive en el mismo contenedor que la Academia. Hay que guardarlas aparte, en un gestor de contraseñas.

Mientras falten la copia externa y las variables guardadas aparte, una pérdida total del servidor no se puede recuperar con este procedimiento. El resto de casos (datos dañados, mudanza, servidor nuevo con el servidor original todavía accesible) sí. Cerrar estos dos pendientes es lo más importante del plan (ver Pendientes operativos).

Objetivos

IndicadorObjetivoMedido en prueba
RPO (pérdida máxima de datos)24 horas: un respaldo diario—
RTO (tiempo hasta volver a estar en línea)Unas 2 horas, contando la construcción de la imagenUnos 13 minutos desde cero (imagen 11 min + restauración 2 min), más el cambio de dirección

Antes de empezar: qué se necesita

NecesarioDónde estáEstado hoy
Un respaldo delta-lms_backup_AAAA-MM-DD_HHMMSS.tar y su .sha256En el servidor: /var/backups/delta-lms (14 días). Fuera del servidor: la copia externaSolo en el mismo servidor. Si el servidor se pierde, los respaldos se pierden con él hasta configurar la copia externa
Las variables de entorno (claves de la base, correo, IA, dirección del sitio)Coolify → recurso → Environment Variables. Copia aparte: gestor de contraseñasSolo en Coolify, que vive en el mismo contenedor. Hay que guardarlas aparte
Acceso al repositorio deltamontero/lms-moodle (privado)GitHub de la empresaLo tiene el equipo de desarrollo
Un servidor Linux con Docker: 4 CPU, 8 GB de RAM, 50 GB o más de disco. Si es un contenedor de Proxmox: no privilegiado con nesting=1 y keyctl=1ProxmoxDisponible: contenedor 12030
La dirección con la que entran los usuarios (MOODLE_WWWROOT)Variable de CoolifyHoy: http://192.168.0.192 (red interna)

Tiempo estimado: mismo servidor, unos 5 minutos. Servidor nuevo, de 1 a 2 horas (la imagen tarda unos 11 minutos en construirse; la restauración, unos 2).

Dónde corre hoy la Academia

Servidor actualServidor nuevo preparado por TI
TipoContenedor LXC de Proxmox 172246, con Coolify dentroContenedor LXC de Proxmox 12030
SistemaPor confirmarUbuntu
RecursosPor confirmar4 CPU, 8 GB de RAM, 4 GB de swap, 250 GB de disco
Opciones del contenedorPor confirmarnesting=1 (comprobar también keyctl=1, que pide el requisito)
Direcciónhttp://192.168.0.192 (red interna de la empresa)192.168.0.191

La publicación de la Academia en Internet con el dominio learning.deltamontero.net detrás de Cloudflare está planificada; todavía no está en uso. Hoy los usuarios entran desde la red interna de la empresa.

Caso A: mismo servidor

Datos dañados o borrados. El servidor y Coolify funcionan; solo hay que volver a los datos de un respaldo. Todo se hace en la terminal del servidor (Coolify → Terminal → localhost), como root.

Elegir el respaldo

El más reciente de antes del problema:

ls -lh /var/backups/delta-lms/

Simular

No cambia nada. Revise en [2/7] Detectando entorno destino que los contenedores sean los de la Academia (ver Siempre primero la simulación):

/opt/delta-lms/infrastructure/scripts/restore.sh --dry-run /var/backups/delta-lms/<archivo>.tar

Restaurar

Pide escribir RESTAURAR para continuar:

/opt/delta-lms/infrastructure/scripts/restore.sh /var/backups/delta-lms/<archivo>.tar

No lance un Redeploy en Coolify mientras se restaura. Los volúmenes persisten entre despliegues.

Verificar

Siga la lista de Verificar que quedó bien.

Caso B: servidor nuevo con Coolify (recomendado)

El servidor original no sirve o se muda la Academia a otro (por ejemplo, del contenedor 172246 al 12030).

1. Preparar el servidor

Ubuntu actualizado, con salida a Internet. En Proxmox: Options → Features con nesting y keyctl, y Start at boot en Yes.

2. Instalar Coolify

Con el instalador oficial (instala también Docker), como root:

curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash

Al terminar, abra http://IP-del-servidor:8000 y cree la cuenta de administrador de Coolify.

3. Conectar el repositorio

En Coolify, agregue el acceso al repositorio privado deltamontero/lms-moodle (GitHub App o llave de despliegue), igual que en el Coolify original. Lo hace quien administra el GitHub de la empresa.

4. Crear el recurso

Nuevo recurso desde el repositorio privado, rama main, tipo Docker Compose con el archivo /docker-compose.yml de la raíz. Sin dominio: se entra por la IP. Más detalle en Despliegue en producción.

5. Cargar las variables de entorno

Desde el gestor de contraseñas o copiándolas del Coolify original, en modo literal. La lista completa está en .env.example y en Variables de entorno. Las imprescindibles:

VariableValor
MYSQL_ROOT_PASSWORD, MYSQL_PASSWORDLas mismas del original (o nuevas: el respaldo trae los datos, no las claves)
MOODLE_WWWROOTLa dirección con la que entrarán los usuarios, exacta (hoy http://192.168.0.192)
MOODLE_ADMIN_PASSWORD, MOODLE_BACKUP_ADMIN_PASSWORDSolo cuentan en una instalación nueva; con el respaldo vuelven las cuentas originales
GROQ_API_KEYClave del chat de IA
MOODLE_SMTP_*, MOODLE_SUPPORT_EMAILCorreo. En una prueba, déjelas vacías o use MOODLE_MAIL_DIVERT_TO (ver Correo (SMTP) después de recuperar)

6. Desplegar una vez

Pulse Deploy. Se construye la imagen y se crean los contenedores y volúmenes. Espere a que moodle y moodle-cron queden en verde (healthy).

7. Traer los scripts al servidor

En Coolify → Terminal → localhost:

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/

8. Traer el respaldo y su .sha256

A /var/backups/delta-lms/ del servidor nuevo. Desde el servidor original, si está accesible por SSH en la red:

mkdir -p /var/backups/delta-lms
scp root@<IP-servidor-original>:/var/backups/delta-lms/<archivo>.tar* /var/backups/delta-lms/

O desde la copia externa (cuando exista):

rclone copy r2crypt:<archivo>.tar /var/backups/delta-lms/
rclone copy r2crypt:<archivo>.tar.sha256 /var/backups/delta-lms/

9. Simular y restaurar

/opt/delta-lms/infrastructure/scripts/restore.sh --dry-run /var/backups/delta-lms/<archivo>.tar
/opt/delta-lms/infrastructure/scripts/restore.sh /var/backups/delta-lms/<archivo>.tar

Comprueba el sha256, detiene Moodle y cron, restaura la base y moodledata, actualiza y vuelve a arrancar (ver Qué hace restore.sh).

10. Si la dirección cambió

Si la dirección es distinta de la del servidor original (por ejemplo, de .192 a .191), ejecute el comando de replace.php que el script muestra al final, para corregir los enlaces absolutos guardados. Tiene esta forma:

docker exec -u www-data <moodle> php /var/www/html/admin/tool/replace/cli/replace.php --search=<origen> --replace=<destino> --non-interactive

11. Activar el respaldo diario

En el servidor nuevo (ver Respaldos):

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

12. Verificar y avisar

Siga la lista de Verificar que quedó bien y, si todo está bien, avise a los usuarios.

Caso C: servidor nuevo sin Coolify

Solo si Coolify no está disponible. En el servidor, con Docker instalado:

Preparar el repositorio

git clone https://github.com/deltamontero/lms-moodle.git && cd lms-moodle
cp .env.example .env

Complete .env con las variables (las mismas del paso 5 del caso B) y COMPOSE_PROJECT_NAME=delta-lms. Si el respaldo se hizo con --with-env y no existe .env, restore.sh lo crea desde env.backup (revíselo después).

Construir la imagen

docker compose build moodle

Simular y restaurar

infrastructure/scripts/restore.sh --dry-run <respaldo>.tar
infrastructure/scripts/restore.sh --yes <respaldo>.tar

El script crea la base, Moodle y el cron, restaura y arranca.

Proxy

Traefik va aparte:

docker compose -f docker-compose.yml --profile standalone up -d traefik

Reactive antes las etiquetas de Traefik comentadas del servicio moodle en docker-compose.yml. La configuración del certificado TLS en este modo es Por confirmar (ver Despliegue en producción).

Terminar como en el caso B

Siga los pasos 10 a 12 del caso B: corregir la dirección si cambió, activar el respaldo diario y verificar.

Verificar que quedó bien

  • La Academia abre en la dirección de siempre y se puede iniciar sesión.
  • Un colaborador ve sus módulos y su avance; un módulo abre sus videos y materiales.
  • Un PDF o una imagen subidos se abren (los archivos volvieron).
  • Un certificado ya emitido se descarga.
  • Coolify muestra moodle, moodle-cron y mariadb en verde, y el super administrador no ve avisos de despliegue ni de cron.
  • El chat de IA responde.
  • Correo: Correos de la Academia → Diagnosticar la conexión y Enviar un correo de prueba (ver Correo (SMTP) después de recuperar).
  • El respaldo diario quedó activo (cat /etc/cron.d/delta-lms-backup).

Si Moodle no terminó de arrancar en el tiempo de espera de restore.sh (900 s por defecto), el script lo avisa: revise los registros del contenedor y luego purgue las cachés:

docker exec -u www-data <moodle> php /var/www/html/admin/cli/purge_caches.php

Volver atrás

Si algo sale mal en una mudanza: el servidor original no se borra ni se modifica durante la mudanza; solo se pone en modo mantenimiento y deja de recibir a los usuarios. Para volver atrás:

  1. Apague el servidor nuevo.
  2. Devuelva la dirección al servidor original.
  3. Quite el modo mantenimiento en el servidor original.

Lo hecho en el servidor nuevo desde el cambio no estará en el original: decídalo en las primeras horas.

Correo (SMTP) después de recuperar

La Academia envía sus correos (bienvenida, recuperar contraseña, códigos de clientes y postulantes, avisos de respaldo) con la cuenta de Office 365 info@deltamontero.com. La configuración del correo no está en el respaldo: sale de las variables de entorno en cada arranque. Si el servidor nuevo no las tiene, la plataforma funciona pero no envía ningún correo (la contraseña se sigue pudiendo recuperar con la cédula).

Variable (Coolify → Environment Variables)Valor
MOODLE_SMTP_HOSTsmtp.office365.com
MOODLE_SMTP_PORT587
MOODLE_SMTP_SECUREtls (STARTTLS)
MOODLE_SMTP_USERinfo@deltamontero.com
MOODLE_SMTP_PASSWORDLa clave de esa cuenta: solo en Coolify y en el gestor de contraseñas, en modo literal (sin interpolar)
MOODLE_SMTP_FROMVacío (el remitente es la misma cuenta: Office 365 rechaza otro remitente)
MOODLE_MAIL_DIVERT_TOVacío en producción. Con una dirección, todos los correos van solo ahí (para pruebas)
MOODLE_SUPPORT_EMAILCorreo del formulario "Contactar con el soporte del sitio"

Después de cargarlas y desplegar:

Abrir la página de correo

Entre como super administrador a Administración del sitio → Usuarios → Correos de la Academia.

Diagnosticar la conexión

Pulse Diagnosticar la conexión. Revisa en orden: nombre del servidor (DNS), salida por el puerto 587, cifrado e inicio de sesión, y dice qué falla y quién lo corrige. No envía ningún correo. El super administrador puede probar además con un usuario y una clave escritos (no se guardan) para saber si la clave guardada en Coolify está distinta.

Enviar un correo de prueba

Use Enviar un correo de prueba a un buzón propio.

Revisar el registro de envíos

En la pestaña Registro de envíos: los correos que quedaron en cola antes del desastre salen solos al configurar el correo, respetando el límite por minuto.

Problemas conocidos:

El diagnóstico diceQué hacer
No conecta al puerto 587El servidor nuevo no tiene permitida la salida por ese puerto: abrirla en el firewall o la red
SMTP AUTH desactivado (5.7.139)El administrador de Microsoft 365 de la empresa lo activa: Usuarios → info@ → Correo → Aplicaciones de correo → SMTP autenticado
Usuario o clave no aceptados (535)Volver a pegar la clave en Coolify (modo literal, sin espacios). Si escribiéndola en el diagnóstico también falla, el administrador de Microsoft 365 revisa en Microsoft Entra → Usuarios → info@ → Registros de inicio de sesión (acceso condicional por ubicación o IP)
Conexión por IPv6 sin salidaLa imagen ya prefiere IPv4; si aparece "Network is unreachable", hacer Redeploy

Atención con dos instalaciones a la vez

En pruebas de recuperación o en una mudanza al contenedor nuevo puede haber dos Academias con los mismos datos:

  • Una copia de prueba no debe tener las variables MOODLE_SMTP_*, o debe tener MOODLE_MAIL_DIVERT_TO con un buzón propio. Si no, los correos en cola salen dos veces, desde las dos instalaciones, a personas reales.
  • En una mudanza: ponga el correo en pausa en el servidor viejo (Correos de la Academia → Pausar los envíos) antes de sacar el último respaldo. Al restaurar, la pausa viene en el respaldo: se reanuda en el servidor nuevo cuando ya atiende a los usuarios.
  • Si se restaura un respaldo de un día anterior, los enlaces para crear contraseña enviados después de ese respaldo dejan de servir. Se vuelven a enviar desde Correos de la Academia → Personal que todavía no entra, o a una persona desde Personal Deltamontero → Editar → Enviar el enlace por correo.

Mudanza a otro contenedor o servidor

Está prevista la mudanza de la Academia del contenedor 172246 (http://192.168.0.192) al contenedor nuevo 12030 (192.168.0.191), que sirve además como prueba de recuperación con datos reales. Resumen de la mudanza, sobre el procedimiento del caso B:

Preparar el destino

Siga los pasos 1 a 7 del caso B en el destino, con MOODLE_MAIL_DIVERT_TO apuntando a un buzón propio o sin las variables de correo mientras se prueba.

Pausar el correo en el servidor viejo

En el servidor viejo: Correos de la Academia → Pausar los envíos, antes de sacar el último respaldo. Los correos quedan en cola. Ponga el servidor viejo en modo mantenimiento para que deje de recibir a los usuarios, sin borrarlo ni modificarlo (ver Volver atrás).

Último respaldo y restauración

Saque el último respaldo en el servidor viejo, cópielo con su .sha256 al destino y restáurelo con restore.sh, primero con --dry-run (pasos 8 y 9 del caso B).

Cambiar la dirección

Cargue en el destino MOODLE_WWWROOT con la dirección definitiva. Si es distinta de la anterior (por ejemplo, de .192 a .191), ejecute replace.php (paso 10 del caso B) e informe a los usuarios la nueva dirección. Verifique ingreso, un módulo, un archivo y un certificado. Cuando la Academia se publique con dominio (planificado), este paso será apuntar el dominio en Cloudflare al destino.

Reanudar el correo

La pausa viene en el respaldo. Cuando el servidor nuevo ya atiende a los usuarios, cargue las variables de correo definitivas (sin desvío), despliegue, diagnostique y pulse Reanudar los envíos en Correos de la Academia.

Activar el respaldo en el destino

Ejecute setup-backup.sh en el servidor nuevo (paso 11 del caso B) y desactive el respaldo del viejo cuando se dé de baja.

La mudanza al contenedor 12030 y la prueba de recuperación con un respaldo real de producción siguen pendientes (ver Pendientes operativos). Cómo se desactiva el respaldo en el servidor viejo (por ejemplo, retirando /etc/cron.d/delta-lms-backup) no está documentado en el repositorio: Por confirmar.

Referencia: restore.sh

Qué hace restore.sh

restore.sh restaura la base de datos y moodledata en un orden pensado para que Moodle nunca arranque con datos a medias:

PasoQué hace
[1/7]Verifica el sha256 del archivo (debe estar el .sha256 al lado) y lee el manifiesto
[2/7]Detecta el entorno destino (contenedores, credenciales, volumen de moodledata) y pide confirmación
[3/7]Detiene moodle y moodle-cron
[4/7]Levanta MariaDB si hace falta, borra y crea la base (con el cotejamiento del respaldo) y carga el volcado
[5/7]Vacía el volumen de moodledata y extrae la copia con un contenedor temporal (Moodle sigue detenido)
[6/7]Arranca moodle y moodle-cron
[7/7]Espera a que Moodle termine su arranque (actualización + configuración) y purga las cachés

Detalles importantes:

  • Por defecto usa solo docker-compose.yml, sin el archivo de desarrollo (que cambiaría la dirección a localhost:8080 y abriría el puerto 8080). --local lo incluye.
  • Si los contenedores son de otro proyecto de Compose (Coolify usa un UUID), cambia solo a docker stop / docker start. --no-compose fuerza ese modo.
  • El contenedor temporal usa la imagen de Moodle (GNU tar y el mismo usuario www-data) y recibe el archivo por la entrada estándar, sin montar rutas del servidor.
  • Si algo falla a medias, Moodle queda detenido a propósito: con la base vacía, su arranque reinstalaría Moodle. Corrija la causa y vuelva a ejecutar el mismo comando.
  • Si la versión de Moodle del respaldo y la de la imagen difieren, avisa: Moodle no admite bajar de versión; si la imagen es más nueva, el arranque hace la actualización.

Opciones

infrastructure/scripts/restore.sh --dry-run backups/delta-lms_backup_X.tar   # simulación
infrastructure/scripts/restore.sh backups/delta-lms_backup_X.tar             # pide escribir RESTAURAR
infrastructure/scripts/restore.sh --yes backups/delta-lms_backup_X.tar       # sin confirmación
infrastructure/scripts/restore.sh --local ...                                # dev: incluye el override
OpciónPara qué
-y, --yesNo pedir confirmación (automatización)
-n, --dry-runSimulación: solo lecturas (checksum, manifiesto, docker inspect); imprime cada comando que modificaría algo, sin ejecutarlo
--localEntorno de desarrollo: incluye docker-compose.override.yml
--no-composeUsar docker stop/start sobre los contenedores detectados
--skip-checksumRestaurar sin .sha256 (no recomendado)
-h, --helpAyuda

Siempre primero la simulación

infrastructure/scripts/restore.sh --dry-run <respaldo>.tar

Revise en el paso [2/7] Detectando entorno destino qué contenedores va a reemplazar: modo (compose o docker), contenedor de MariaDB y base (con su número de tablas actual), contenedor de Moodle y su versión, contenedor del cron y volumen de moodledata. La simulación no cambia nada.

Regla de seguridad: COMPOSE_PROJECT_NAME

Si se define COMPOSE_PROJECT_NAME (como variable o en .env), los scripts solo usan los contenedores de ese proyecto; si no existen, restore.sh los crea (servidor nuevo). Sin definirlo, como en Coolify (donde no hay .env en el servidor), buscan los de cualquier proyecto.

Si en el servidor hay más de una instalación (por ejemplo, una de prueba junto a la de producción), defina siempre COMPOSE_PROJECT_NAME. Antes de esta regla, al restaurar en un proyecto aparte, el script tomaba los contenedores de otro proyecto en marcha y restauraba sobre él (hallazgo de la prueba del 2026-10-06, ya corregido).

Además, si no se detecta Moodle, el script no acepta la base de datos ni el cron de otros proyectos, y se niega a seguir si la base o el cron no pertenecen al mismo proyecto que Moodle.

Pruebas realizadas

Los resultados de las pruebas de respaldo y recuperación (tiempos, conteos verificados y hallazgos) están en Registro de pruebas.

En esta página