Hay una mejora de rendimiento en Moodle que suele ser, por sí sola, la más rápida de aplicar y la de mayor impacto en el tiempo de carga: activar OPcache correctamente. Y hay un error asociado a ella tan común que prácticamente todo administrador de Moodle lo comete al menos una vez: actualizar un plugin, verlo instalado en el panel, y descubrir que “no hace nada”: porque OPcache seguía sirviendo el código compilado de antes del cambio.
En esta guía vas a ver qué es exactamente OPcache, cómo configurarlo para Moodle, y cómo evitar (o resolver) ese error concreto.
Qué es OPcache
OPcache es un acelerador integrado en PHP que almacena en memoria compartida el bytecode ya compilado de los archivos PHP, evitando que el servidor tenga que volver a analizar y compilar ese código en cada petición. Una instalación estándar de Moodle contiene miles de archivos PHP, sin OPcache, cada carga de página implica recompilar buena parte de ese código desde cero, una y otra vez.
Configuración recomendada para producción
Este es un punto de partida sensato para un Moodle de producción con volumen real de plugins. Cada línea va comentada; debajo explicamos el porqué de los valores que más importan. Se pone normalmente en un .ini propio dentro de conf.d (por ejemplo /etc/php/8.2/fpm/conf.d/10-opcache.ini) o en el php.ini del pool de PHP-FPM.
; Activa OPcache y su uso también en línea de comandos
opcache.enable=1
opcache.enable_cli=1
; Memoria compartida para el bytecode compilado
opcache.memory_consumption=256
opcache.interned_strings_buffer=16
; Número máximo de archivos cacheables a la vez
opcache.max_accelerated_files=16229
; Gestión de cambios en disco (ver sección de despliegue)
opcache.validate_timestamps=0
opcache.revalidate_freq=0
; Moodle necesita los comentarios de anotación
opcache.save_comments=1
; Reciclado de memoria fragmentada
opcache.max_wasted_percentage=10
opcache.memory_consumption(en MB): el espacio en memoria compartida reservado para el bytecode compilado. El valor por defecto de PHP es 128, y una instalación estándar de Moodle ronda ya los 150 MB solo en archivos PHP del núcleo, sin contar plugins ni el código demoodledata, que también entra. Con el defecto de 128 la caché se llena y empieza a descartar entradas casi de inmediato. 256 deja margen cómodo para el núcleo más un buen número de plugins; sitios muy grandes pueden necesitar 384 o 512. La forma correcta de fijarlo no es adivinar, sino medir la memoria realmente usada (más abajo).opcache.max_accelerated_files: el número máximo de archivos que OPcache mantiene en caché a la vez. Aquí está el clásico cuello de botella de Moodle: una instalación con muchos plugins supera con holgura los 10.000 archivos PHP, y el valor por defecto se queda corto. Un detalle poco conocido: PHP redondea este número hacia arriba hasta el siguiente primo de una lista fija (…3907, 7963, 16229, 32531, 65407…). Por eso ponemos16229en vez de16000: es el valor real que PHP va a usar, así que conviene escribirlo tal cual para que lo que lees en la config coincida con lo que ves al medir.opcache.interned_strings_buffer(en MB): memoria dedicada a almacenar una sola copia de cada cadena repetida (nombres de clase, funciones, etc.). Moodle define muchísimas cadenas idénticas entre archivos; subirlo de los 8 MB por defecto a 16 evita que este buffer se sature antes que el principal.opcache.save_comments: déjalo en 1. Moodle depende de las anotaciones en los docblocks (@param,@returny anotaciones internas) para parte de su funcionalidad y para las pruebas. Si lo pones a 0 para “ahorrar memoria”, romperás cosas de forma sutil y difícil de diagnosticar.opcache.enable_cli: activa OPcache también para procesos de línea de comandos. Importa porque el cron de Moodle se ejecuta por CLI; con esto acelera igual que las peticiones web.opcache.max_wasted_percentage: cuando OPcache invalida o reemplaza archivos deja “huecos” de memoria desperdiciada. Al superar este porcentaje, PHP programa un reinicio del caché para desfragmentarlo. Subirlo de 5 a 10 reduce la frecuencia de esos reinicios automáticos en sitios que actualizan código de vez en cuando.
Tabla rápida de ajustes
| Ajuste | Qué hace | Valor orientativo (Moodle) |
|---|---|---|
opcache.enable | Activa el acelerador | 1 |
opcache.enable_cli | OPcache también para el cron (CLI) | 1 |
opcache.memory_consumption | MB de memoria para bytecode | 256 (grandes: 384-512) |
opcache.interned_strings_buffer | MB para cadenas deduplicadas | 16 |
opcache.max_accelerated_files | Máx. de archivos cacheados | 16229 (primo real) |
opcache.validate_timestamps | ¿Comprobar cambios en disco? | 0 en prod (con reset en deploy) |
opcache.revalidate_freq | Cada cuántos seg. revalidar | 0 si timestamps=0; 60 si =1 |
opcache.save_comments | Conserva docblocks | 1 (obligatorio en Moodle) |
opcache.max_wasted_percentage | Umbral de reinicio por fragmentación | 10 |
El ajuste que da más rendimiento y más problemas si no se gestiona bien
opcache.validate_timestamps=0 le dice a PHP que deje de comprobar si los archivos han cambiado en disco, sirviendo siempre el bytecode ya compilado sin verificación adicional. Esto mejora el rendimiento de forma notable, pero tiene una consecuencia que hay que gestionar activamente: tras cualquier despliegue de código, actualización de plugin o cambio de configuración de archivos, OPcache no se entera del cambio por sí solo.
El síntoma clásico: actualizas un plugin, lo ves correctamente instalado en el panel de administración de Moodle, pero al usarlo, sigue comportándose exactamente igual que antes, como si la actualización no hubiera hecho nada. La causa casi siempre es esta: el servidor sigue sirviendo el bytecode compilado antes del cambio.
La solución, en cada despliegue:
php -r "opcache_reset();"
O, alternativamente, reiniciar el servicio PHP-FPM:
systemctl restart php-fpm
Cualquiera de las dos acciones fuerza a OPcache a descartar el caché anterior y recompilar el código actualizado en la siguiente petición.
Por qué no basta con “esperar un poco”: con validate_timestamps=0, PHP directamente deja de hacer stat() sobre los archivos. No es que revise tarde el disco, es que no lo revisa nunca. La fecha de modificación de un archivo puede cambiar mil veces y OPcache seguirá sirviendo la versión que compiló la primera vez, hasta el fin de los tiempos o hasta que alguien vacíe la caché. Por eso el reset no es opcional ni “por si acaso”: es el único mecanismo que le comunica el cambio. Lo suyo es convertirlo en el último paso de tu proceso de despliegue, un hook post-deploy que ejecute el opcache_reset() (o el systemctl reload php-fpm, que preserva conexiones mejor que restart) justo después de subir el código y purgar las cachés de Moodle con php admin/cli/purge_caches.php. Si lo haces a mano, ponlo en la misma checklist que la purga de cachés: van juntos.
Alternativa más segura para entornos con despliegues frecuentes
Si tu Moodle recibe actualizaciones de plugins o cambios de código con mucha frecuencia, y el equipo que hace esos despliegues no siempre recuerda el paso de reinicio, una alternativa más conservadora es mantener opcache.validate_timestamps=1 con un opcache.revalidate_freq bajo (por ejemplo, 60 segundos). Esto sacrifica una pequeña parte del rendimiento máximo a cambio de que los cambios de código se detecten automáticamente sin necesidad de un paso manual adicional, una opción razonable si el proceso de despliegue no está completamente automatizado o documentado.
Cómo medir el hit rate y si la caché se está llenando
Configurar OPcache “a ojo” y no volver a mirarlo es la mitad del trabajo. Los valores de arriba son un punto de partida, no una verdad universal: el número de plugins de tu Moodle decide cuánta memoria y cuántos archivos necesitas de verdad. Para saberlo tienes que medir, y OPcache expone toda la información que hace falta.
La función clave es opcache_get_status(). Un vistazo rápido desde consola:
php -r "print_r(opcache_get_status(false)['opcache_statistics']);"
Gotcha importante: el proceso de línea de comandos tiene su propia instancia de OPcache, separada de la de PHP-FPM. Lo que ves por CLI no es lo que sirve tu web. Para medir el caché real del sitio necesitas ejecutar opcache_get_status() a través del SAPI web, una pequeña página de estado protegida, o una herramienta de panel como opcache-gui (el proyecto amnuts/opcache-gui), que te da hit rate, memoria y número de scripts en tiempo real desde el navegador. Moodle también refleja parte de esto en su propio informe de rendimiento del área de administración, que avisa si OPcache está desactivado o mal dimensionado.
Los tres números que de verdad importan:
opcache_hit_rate: el porcentaje de peticiones de bytecode servidas desde caché. En régimen normal, con el sitio ya “caliente”, debería estar por encima del 99 %. Si se queda en 80-90 % de forma sostenida, OPcache está recompilando cosas que debería tener guardadas: normalmente falta memoria o faltan huecos de archivos.num_cached_keysfrente amax_cached_keys: si el primero se acerca al segundo, has tocado techo demax_accelerated_files. OPcache empieza a expulsar archivos para meter otros, el hit rate baja y las páginas que dependen de los archivos expulsados vuelven a compilar en cada visita. La solución es subirmax_accelerated_filesal siguiente primo de la lista (de 16229 a 32531, por ejemplo) y recargar PHP-FPM.oom_restarts(dentro deopcache_statistics): cuenta cuántas veces OPcache se ha reiniciado por quedarse sin memoria (out of memory). Si este contador sube con el tiempo,memory_consumptiones demasiado bajo: cada reinicio vacía toda la caché de golpe y provoca un pico de recompilación. Súbelo (256 → 384 → 512) hasta queoom_restartsse quede en cero.
En la práctica, la rutina sana es simple: tras unas horas de tráfico real, mira esos tres valores. Hit rate alto, memoria libre con holgura y num_cached_keys lejos del máximo significan que la config es correcta. Cualquier otra cosa te dice exactamente qué palanca tocar. Y si con OPcache bien ajustado tu sitio sigue arrastrándose, el problema está en otro sitio: repasa las causas más comunes de que un Moodle vaya lento, porque OPcache acelera el código PHP, no las consultas a base de datos ni el acceso a moodledata.
Diferencias entre PHP 7 y PHP 8
Con los años, los valores por defecto de OPcache han ido mejorando, y PHP 8 llega ya con una base más sensata que las primeras versiones de la rama 7. En PHP 7.0-7.2, por ejemplo, el límite de archivos por defecto era mucho más bajo, así que en instalaciones antiguas subir max_accelerated_files daba un salto de rendimiento inmediato. A partir de PHP 7.3 el defecto se sitúa en 10.000, y ahí sigue en PHP 8: mejor, pero todavía justo para un Moodle con muchos plugins, que es precisamente el ajuste que más manos pide.
La novedad real de PHP 8 no son los valores por defecto de los parámetros clásicos (memory_consumption sigue en 128, que se queda corto igual), sino la aparición del JIT (opcache.jit), un compilador que traduce el bytecode a código máquina. Suena prometedor, pero conviene la honestidad: para una carga de trabajo web típica de Moodle, muy ligada a base de datos y no a cálculo puro de CPU, el JIT aporta poco o nada y añade complejidad. La recomendación práctica es dejarlo en su valor conservador por defecto y concentrar el esfuerzo en lo que sí mueve la aguja: memoria, número de archivos y la disciplina de reset en cada despliegue.
Resumen: en PHP 8 tienes menos margen de mejora “gratis” que arrastrando un PHP 7 antiguo, pero los dos parámetros que de verdad importan en Moodle (memory_consumption y max_accelerated_files) siguen necesitando que los subas a mano por encima del defecto. El JIT es una distracción para este caso de uso.
Errores comunes con OPcache en Moodle
- Activar
validate_timestamps=0sin establecer un proceso de reinicio tras cada despliegue, generando confusión sobre por qué las actualizaciones “no funcionan”. - Dejar
opcache.memory_consumptionen el valor por defecto, demasiado bajo para el volumen real de archivos PHP de Moodle con plugins instalados. - No comprobar el hit rate de la caché, sin saber si la configuración actual realmente está siendo efectiva.
- Desactivar
opcache.save_comments, rompiendo funcionalidad interna de Moodle que depende de esas anotaciones.
Preguntas frecuentes
¿Por qué mi plugin actualizado no parece hacer nada?
Muy probablemente OPcache sigue sirviendo el bytecode compilado antes de la actualización. Ejecuta opcache_reset() o reinicia PHP-FPM tras cada despliegue de código para resolverlo.
¿Debo desactivar OPcache durante el desarrollo?
Es habitual usar opcache.validate_timestamps=1 con revalidación frecuente en entornos de desarrollo, donde el código cambia constantemente, reservando validate_timestamps=0 para producción.
¿Cuánta memoria debería asignar a OPcache? Como referencia, el código PHP de una instalación estándar de Moodle ronda los 150 MB solo en el núcleo; conviene asignar memoria con margen suficiente sobre ese volumen, considerando también plugins instalados.
¿OPcache afecta al cron de Moodle?
Sí, activar OPcache también para la ejecución por línea de comandos (opcache.enable_cli) acelera la ejecución del cron, igual que acelera las peticiones web normales.
¿Necesito reiniciar el servidor completo tras un despliegue, o solo PHP-FPM?
Basta con reiniciar PHP-FPM (o ejecutar opcache_reset()), sin necesidad de reiniciar el servidor completo.
¿OPcache sustituye a Redis? No, son complementarios: OPcache acelera la ejecución del código PHP en sí, mientras que Redis acelera el acceso a datos de sesión y caché de aplicación, ambos atacan cuellos de botella distintos.
¿Cómo sé si mi configuración actual de OPcache es efectiva?
Revisando el ratio de aciertos de caché con herramientas de monitorización de PHP, y confirmando que opcache.max_accelerated_files es suficiente para el volumen real de archivos de tu instalación.
¿Por qué el hit rate que veo por línea de comandos no cuadra con la web?
Porque son dos cachés distintas. El proceso de CLI (donde corre el cron) tiene su propia instancia de OPcache, independiente de la de PHP-FPM que sirve las páginas. Si quieres medir el caché que ven tus usuarios, ejecuta opcache_get_status() a través del SAPI web (una página de estado u opcache-gui), no desde php -r en la terminal.
¿Qué valor exacto pongo en max_accelerated_files?
Uno de los primos de la lista interna de PHP (…7963, 16229, 32531, 65407…), porque PHP redondea hacia arriba de todas formas. Para un Moodle con plugins, 16229 es un buen punto de partida; si al medir ves que num_cached_keys se acerca al máximo, sube al siguiente primo. Escribir el primo exacto evita sorpresas al comparar la config con lo que reportan las estadísticas.
¿Tengo que reconfigurar OPcache al actualizar la versión de Moodle?
La configuración no, pero sí debes hacer el reset de OPcache como parte de la actualización, igual que en cualquier despliegue de código. Una subida de versión toca cientos de archivos: sin opcache_reset() (o recarga de PHP-FPM) con validate_timestamps=0, arriesgas a servir una mezcla del código viejo y el nuevo. Si acabas de sumar muchos plugins, aprovecha para revisar que max_accelerated_files sigue con margen.
Conclusión
OPcache es, probablemente, la mejora de rendimiento con mejor relación esfuerzo-resultado que puedes aplicar a Moodle, pero exige un proceso claro tras cada despliegue si usas la configuración más agresiva (validate_timestamps=0). Sin ese proceso, la mejora de velocidad viene acompañada de confusión cada vez que se actualiza algo y “parece que no hace efecto”.
Si prefieres que alguien gestione esto de forma sistemática en cada despliegue, en Avantys lo incluimos como parte del mantenimiento de cada Moodle que gestionamos. Puedes pedir una auditoría gratuita en Moodle Gestionado.
Artículos relacionados
- Mantenimiento y hosting Moodle: guía completa
- Por qué mi Moodle va lento: causas más comunes
- Redis para Moodle: caché en memoria explicado
- Actualizar PHP en Moodle sin romper plugins
¿Y si tu Moodle lo lleváramos nosotros?
Actualizaciones, backups verificados, rendimiento y trazabilidad FUNDAE, gestionado de forma continua por una persona que responde. Auditoría gratuita de tu plataforma actual.