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).
| Servicio | Contenedor | Imagen | Función | Reinicio |
|---|---|---|---|---|
mariadb | delta-lms_db | mariadb:11.4 | Base de datos de Moodle | unless-stopped |
moodle | delta-lms_app | delta-lms/moodle:4.5 (construida desde el repositorio) | Sitio web: Apache + PHP 8.3 + Moodle | unless-stopped |
moodle-cron | delta-lms_cron | delta-lms/moodle:4.5 (la misma) | Tareas programadas de Moodle | unless-stopped |
traefik | delta-lms_traefik | traefik:v3 | Proxy 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_PASSWORDyMYSQL_PASSWORD(obligatorias, sin valor por defecto),MYSQL_DATABASE(por defectomoodle) yMYSQL_USER(por defectomoodleuser). - 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_datamontado 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 losMYSQL_*), 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:
moodledataen/var/www/moodledata. No se monta/var/www/html: el código y los plugins están dentro de la imagen. - Depende de que
mariadbesté sano (condition: service_healthy). - Punto de entrada:
/entrypoint.sh(espera a la base, generaconfig.php, instala o actualiza, aplica la configuración y arranca Apache). Ver Configuración automática.
moodle-cron
- Misma imagen que
moodle. Lleva tambiénbuild: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 mismoconfig.phpque el sitio (conwrite-config.sh), espera a que exista la tablamdl_configy entra en un ciclo que ejecutaadmin/cli/cron.phpcada 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
| Servicio | Prueba | Intervalo | Margen inicial |
|---|---|---|---|
mariadb | healthcheck.sh --su-mysql --connect --innodb_initialized | 10 s (10 reintentos) | 30 s |
moodle | curl a http://localhost/login/index.php; sano si responde 200 o 30x | 60 s (3 reintentos) | 900 s |
moodle-cron | Existe /tmp/cron-last-ok y tiene menos de 10 minutos | 60 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
| Volumen | Montado en | Contenido | ¿En el respaldo? |
|---|---|---|---|
mariadb_data | mariadb:/var/lib/mysql | Toda la base de datos de Moodle | Sí, como volcado database.sql.gz |
moodledata | moodle y moodle-cron: /var/www/moodledata | Archivos subidos, cachés, sesiones, temporales | Sí (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
| Parche | Qué hace | Si falla |
|---|---|---|
patch-ai-role-injection.php | Inyecta el perfil del usuario en el prompt del chat de IA | Detiene el build |
patch-ai-agent.php | Engancha el agente de acciones al chat (después del anterior) | Detiene el build |
patch-policy-consentpage.php | Acuerdo del sitio en una sola página | Detiene el build |
patch-forgot-password.php | "¿Olvidó su contraseña?" sin búsqueda por correo | Detiene el build |
patch-welcome-name.php | Saludo con primer nombre y primer apellido | Detiene el build |
patch-ai-chat-everywhere.php | Chat de IA disponible en todas las páginas | Detiene el build |
patch-cedula-field.php | Campo "Cédula" arriba y obligatorio en el formulario de usuario | Detiene el build |
patch-cert-completion-gate.php | Candado real del certificado (sin el bypass de gestores) | Detiene el build |
patch-ai-chat-copy.php | Botón copiar del chat también en HTTP | Cosmético, nunca detiene |
patch-ai-chat-ui.php | Expone nombre, contexto y límite diario del chat al HTML | Cosmético, nunca detiene |
patch-video-completion.php | Quita el confeti y el mensaje "Congratulations" al terminar un video | Cosmé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
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).
Infraestructura
Servidor de producción (contenedor LXC de Proxmox), Coolify, acceso por la red interna, Cloudflare y dominio planificados, entorno local, recursos y manejo de la hora.
Datos y almacenamiento
Qué vive en MariaDB, qué vive en moodledata, qué está en el respaldo y qué queda fuera del servidor.