Academia Deltamontero · Documentación
Arquitectura

Contenedores

Servicios de docker-compose, healthchecks, volúmenes y qué lleva la imagen de Moodle.

Servicios

docker-compose.yml define cuatro servicios en la red delta_network (tipo bridge). Los nombres de contenedor usan COMPOSE_PROJECT_NAME (por defecto delta-lms).

ServicioContenedorImagenFunciónReinicio
mariadbdelta-lms_dbmariadb:11.4Base de datos de Moodleunless-stopped
moodledelta-lms_appdelta-lms/moodle:4.5 (construida desde el repositorio)Sitio web: Apache + PHP 8.3 + Moodleunless-stopped
moodle-crondelta-lms_crondelta-lms/moodle:4.5 (la misma)Tareas programadas de Moodleunless-stopped
traefikdelta-lms_traefiktraefik:v3Proxy inverso solo para recuperación sin Coolify (perfil standalone)unless-stopped

En producción con Coolify no se levanta traefik: Coolify usa su propio Traefik. El bloque de ChromaDB que aparece comentado en el compose pertenece al plan anterior de RAG y ya no aplica (ver Inteligencia artificial).

mariadb

  • Variables: MYSQL_ROOT_PASSWORD y MYSQL_PASSWORD (obligatorias, sin valor por defecto), MYSQL_DATABASE (por defecto moodle) y MYSQL_USER (por defecto moodleuser).
  • Parámetros: --innodb-buffer-pool-size=1G, --character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci (el mismo cotejamiento que las tablas de Moodle).
  • Límite de memoria: 2 GB.
  • Volumen: mariadb_data montado en /var/lib/mysql.
  • No publica puertos: solo es accesible dentro de delta_network.

moodle

  • Recibe por entorno la conexión a la base (MOODLE_DB_*, que el compose arma a partir de los MYSQL_*), la dirección pública (MOODLE_WWWROOT), la cuenta inicial de administración, la identidad del sitio, el correo (MOODLE_SMTP_*, MOODLE_MAIL_DIVERT_TO, MOODLE_NOREPLY_EMAIL, MOODLE_SUPPORT_EMAIL) y los secretos de IA y de la cuenta de respaldo (GROQ_API_KEY, GEMINI_API_KEY, MOODLE_BACKUP_ADMIN_PASSWORD).
  • Límite de memoria: 3 GB.
  • Volumen: moodledata en /var/www/moodledata. No se monta /var/www/html: el código y los plugins están dentro de la imagen.
  • Depende de que mariadb esté sano (condition: service_healthy).
  • Punto de entrada: /entrypoint.sh (espera a la base, genera config.php, instala o actualiza, aplica la configuración y arranca Apache). Ver Configuración automática.

moodle-cron

  • Misma imagen que moodle. Lleva también build: en el compose para que Coolify sepa que la imagen es local y no intente descargarla de Docker Hub.
  • Punto de entrada: /cron-entrypoint.sh. Genera el mismo config.php que el sitio (con write-config.sh), espera a que exista la tabla mdl_config y entra en un ciclo que ejecuta admin/cli/cron.php cada 60 segundos.
  • Solo recibe las variables de base de datos y MOODLE_WWWROOT.
  • Límite de memoria: 1 GB.
  • Volumen: el mismo moodledata.
  • Nunca entra en un bucle de reinicios: si una ejecución falla, escribe un error visible en el registro y sigue intentando. Si durante 10 ciclos seguidos Moodle responde "upgrade pending", avisa que la imagen del cron no coincide con la del sitio y que hay que redesplegar.

El ajuste cronclionly = 1 impide lanzar el cron desde la web: solo corre desde este contenedor.

Healthchecks

ServicioPruebaIntervaloMargen inicial
mariadbhealthcheck.sh --su-mysql --connect --innodb_initialized10 s (10 reintentos)30 s
moodlecurl a http://localhost/login/index.php; sano si responde 200 o 30x60 s (3 reintentos)900 s
moodle-cronExiste /tmp/cron-last-ok y tiene menos de 10 minutos60 s (3 reintentos)900 s

El margen inicial de 15 minutos de moodle y moodle-cron cubre el primer arranque, en el que se instala y configura Moodle. Coolify muestra el servicio como "unhealthy" cuando falla la prueba.

Además, dentro de Moodle el super administrador ve un aviso si el cron no se ejecuta hace más de 15 minutos (ver Seguridad y Operación).

Volúmenes

VolumenMontado enContenido¿En el respaldo?
mariadb_datamariadb:/var/lib/mysqlToda la base de datos de MoodleSí, como volcado database.sql.gz
moodledatamoodle y moodle-cron: /var/www/moodledataArchivos subidos, cachés, sesiones, temporalesSí (moodledata.tar.gz), sin cache, localcache, sessions, temp, trashdir

docker compose down conserva los volúmenes; docker compose down -v los borra (reinicio total).

La imagen de Moodle

La imagen se construye con infrastructure/moodle/Dockerfile a partir de moodlehq/moodle-php-apache:8.3. Todo lo que necesita para funcionar va dentro, para poder recuperar el servicio sin depender de repositorios externos en el momento del desastre.

Capas, en orden

Sistema. wget, unzip, git, curl, python3 y las librerías Python pypdf, python-docx y openpyxl, que usan el importador de módulos desde documentos, la importación de personal y los informes en Word.

PHP y Apache para producción. Archivo zz-delta.ini: display_errors=Off, log_errors=On, memory_limit=512M, upload_max_filesize y post_max_size de 512M, max_execution_time=300, date.timezone=America/Guayaquil. Apache niega el acceso web a behat, vendor, node_modules y .git.

Red. Preferencia por IPv4 en /etc/gai.conf (correo de Office 365).

Moodle core. Clonado con git-at en un commit exacto en /var/www/html.

Plugins de terceros. Cada uno en un commit o etiqueta fija (ver Plugins y componentes).

Scripts de arranque y configuración. Todos los .sh, .php, .py y .json de infrastructure/moodle/ se copian a la raíz / en una sola capa (Docker limita la imagen a 127 capas). Solo entran los archivos autorizados en .dockerignore, que funciona como lista blanca. También infrastructure/moodle/checkpoints/.

Respaldo y recuperación. docker-compose.yml y infrastructure/scripts/ se copian a /opt/delta-lms, para instalarlos en el host sin clonar el repositorio privado.

Recursos de marca. Logos (/opt/delta-logos, más favicon.ico en la raíz web), fondo del certificado, imagen de fondo, la fuente Century Gothic (/opt/delta-fonts), la plantilla de informes (/opt/delta-reports) y el inventario de contenido academia_dm_contenido.json (/opt/academia-dm).

Plugins propios. local_deltacerts, local_deltamodules, quizaccess_deltagate, local_candidate_enrollment y los tres dataformat. Traducciones al español de plugins de terceros en infrastructure/moodle/lang/es/.

Parches de código. Los scripts patch-*.php modifican puntos concretos de Moodle y de plugins de terceros. Al final, verify-patches.php comprueba que los parches importantes quedaron aplicados: si falta uno, el build falla. Luego se normalizan los finales de línea de los .sh y se les da permiso de ejecución.

Parches aplicados en la construcción

ParcheQué haceSi falla
patch-ai-role-injection.phpInyecta el perfil del usuario en el prompt del chat de IADetiene el build
patch-ai-agent.phpEngancha el agente de acciones al chat (después del anterior)Detiene el build
patch-policy-consentpage.phpAcuerdo del sitio en una sola páginaDetiene el build
patch-forgot-password.php"¿Olvidó su contraseña?" sin búsqueda por correoDetiene el build
patch-welcome-name.phpSaludo con primer nombre y primer apellidoDetiene el build
patch-ai-chat-everywhere.phpChat de IA disponible en todas las páginasDetiene el build
patch-cedula-field.phpCampo "Cédula" arriba y obligatorio en el formulario de usuarioDetiene el build
patch-cert-completion-gate.phpCandado real del certificado (sin el bypass de gestores)Detiene el build
patch-ai-chat-copy.phpBotón copiar del chat también en HTTPCosmético, nunca detiene
patch-ai-chat-ui.phpExpone nombre, contexto y límite diario del chat al HTMLCosmético, nunca detiene
patch-video-completion.phpQuita el confeti y el mensaje "Congratulations" al terminar un videoCosmético, nunca detiene

verify-patches.php revisa ocho marcas: perfil en el chat, agente del chat, política en una página, recuperación sin correo, saludo, chat en todas las páginas, campo Cédula y certificado solo con el módulo completo.

Estructura de la imagen

entrypoint.sh, cron-entrypoint.sh, write-config.sh, moodle-config.sh
*.php, *.py, manual-academia.json (scripts de configuración y aprovisionamiento)

Reconstruir la imagen

Cualquier cambio en el Dockerfile, en los plugins propios, en los scripts o en los recursos exige reconstruir la imagen (docker compose up -d --build en local; Redeploy en Coolify). Como moodle y moodle-cron comparten imagen, ambos deben quedar con la misma versión: si no, el cron reporta "upgrade pending".

La construcción completa tardó unos 11 minutos en la prueba de recuperación del 2026-10-06 (docs/drp.md).

En esta página