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:

  1. No opcode cache or session cache — PHP recompiles scripts on every request
  2. Moodle cron not running — Background tasks pile up and execute during page loads
  3. No Redis/Memcached for sessions — File-based sessions don’t scale
  4. Default PHP-FPM worker count is too low — Concurrent users queue up
  5. 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:

  1. Go to Site Administration → Appearance → Themes → Theme Settings
  2. Uncheck Theme designer mode
  3. 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->cachedir to a fast filesystem (SSD or tmpfs) if possible
  • Review scheduled tasks at Site Administration → Server → Scheduled Tasks — disable any you don’t need

Comments