Moodle: Slow Performance — Fix
The Problem
Your self-hosted Moodle instance is painfully slow. Pages take 5-15 seconds to load, the dashboard is sluggish, course pages hang, and the admin panel feels unresponsive. This is especially common with Docker deployments where default settings aren’t tuned for production workloads.
Updated March 2026: Verified with latest Docker images and configurations.
Common symptoms:
Page loading time: 8-15 seconds
"Maximum execution time exceeded" errors
Database connection timeout warnings
High CPU usage from PHP-FPM processes
Browser showing "waiting for server..." for extended periods
The Cause
Moodle’s default Docker configuration is designed for development, not production. Several factors compound:
- No opcode cache or session cache — PHP recompiles scripts on every request
- Moodle cron not running — Background tasks pile up and execute during page loads
- No Redis/Memcached for sessions — File-based sessions don’t scale
- Default PHP-FPM worker count is too low — Concurrent users queue up
- MariaDB/MySQL not tuned — Default
innodb_buffer_pool_size(128 MB) is far too small for Moodle’s query patterns
The Fix
Method 1: Enable Redis Caching (Biggest Impact)
Add Redis to your Docker Compose and configure Moodle to use it for caching:
services:
redis:
image: redis:7-alpine
container_name: moodle-redis
volumes:
- redis-data:/data
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
volumes:
redis-data:
Then configure Moodle’s config.php to use Redis (add before the require_once line at the bottom):
// Redis cache store
$CFG->session_handler_class = '\core\session\redis';
$CFG->session_redis_host = 'redis';
$CFG->session_redis_port = 6379;
$CFG->session_redis_database = 0;
$CFG->session_redis_prefix = 'moodle_sess_';
After restarting, go to Site Administration → Plugins → Caching → Configuration and add a Redis cache store. Set it as the default for Application and Session caches.
Method 2: Configure Moodle Cron
Moodle requires a cron job running every minute. Without it, background tasks (cache cleanup, email processing, grade calculations) execute during user page loads, causing massive delays.
Add a cron container to your Docker Compose:
services:
moodle-cron:
image: bitnami/moodle:4.5.3
container_name: moodle-cron
entrypoint: ["/bin/sh", "-c"]
command:
- |
while true; do
php /bitnami/moodle/admin/cli/cron.php
sleep 60
done
volumes:
- moodle-data:/bitnami/moodle
- moodledata:/bitnami/moodledata
depends_on:
- moodle
restart: unless-stopped
Verify cron is running:
docker compose logs moodle-cron --tail 20
You should see output like Cron script completed correctly every minute.
Method 3: Tune PHP-FPM Settings
Create a custom PHP-FPM configuration to increase worker capacity:
; custom-php.ini
; Increase memory limit for Moodle (default 128M is too low)
memory_limit = 512M
; Enable and configure OPcache
opcache.enable = 1
opcache.memory_consumption = 256
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 60
opcache.interned_strings_buffer = 16
opcache.fast_shutdown = 1
; Increase execution limits
max_execution_time = 300
max_input_time = 300
max_input_vars = 5000
; File upload limits
upload_max_filesize = 256M
post_max_size = 256M
Mount this configuration in your Moodle container:
services:
moodle:
volumes:
- ./custom-php.ini:/opt/bitnami/php/etc/conf.d/custom-php.ini:ro
Method 4: Tune MariaDB/MySQL
The default innodb_buffer_pool_size of 128 MB is inadequate. For a server with 4+ GB RAM, set it to 1-2 GB:
services:
db:
image: mariadb:11.4
command:
- --innodb-buffer-pool-size=1G
- --innodb-log-file-size=256M
- --innodb-flush-method=O_DIRECT
- --innodb-flush-log-at-trx-commit=2
- --query-cache-type=1
- --query-cache-size=128M
- --max-connections=200
- --tmp-table-size=128M
- --max-heap-table-size=128M
Method 5: Disable Moodle Theme Designer Mode
In production, theme designer mode forces CSS and JavaScript recompilation on every page load. Disable it:
- Go to Site Administration → Appearance → Themes → Theme Settings
- Uncheck Theme designer mode
- Purge caches: Site Administration → Development → Purge all caches
Or via CLI:
docker compose exec moodle php /bitnami/moodle/admin/cli/cfg.php --name=themedesignermode --set=0
docker compose exec moodle php /bitnami/moodle/admin/cli/purge_caches.php
Prevention
- Always run Redis alongside Moodle in production — it’s not optional for acceptable performance
- Always configure cron — Moodle’s architecture depends on it
- Monitor with
php admin/cli/checks.php— this reports configuration issues including missing cron and cache problems - Set
$CFG->cachedirto a fast filesystem (SSD or tmpfs) if possible - Review scheduled tasks at Site Administration → Server → Scheduled Tasks — disable any you don’t need
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments