Infrastructure & Scaling

Moodle Performance Tuning: Caching, Database & Server Config

A technical breakdown of where Moodle installs actually slow down under real load — PHP-FPM and opcache settings, MUC caching with Redis, database indexing, the cron job, and the scaling issues that only show up once enrolment numbers get large.

Redis / Memcached MUCDatabase & Cron Tuning8+ Years Building on Moodle
Moodle Caching Layers

Moodle Slows Down as Enrolment Numbers Grow — By Default

A default Moodle install tuned for a handful of pilot courses behaves very differently once thousands of learners, large logstore tables, and concurrent quiz attempts show up. Most performance problems we're called in for aren't Moodle being slow by nature — they're default configuration never revisited as the site grew.

File-Based Caching at Scale

Default MUC file caching doesn't share state across multiple web servers, and gets slower as cache size grows.

Unindexed, Unpruned Tables

Logstore and grade tables that grow for years without index review or retention policy.

Undersized PHP Pools

PHP-FPM and opcache settings left at distro defaults instead of sized against real memory and traffic.

The Four Places Moodle Performance Actually Comes From

Every slow Moodle site we've diagnosed traces back to some combination of these four layers.

PHP-FPM Pool Tuning

Undersized pm.max_children starves Moodle under concurrent logins, while oversized pools exhaust server memory. Pool size has to be set against actual per-process memory usage, not a guess.

Opcache Configuration

Without a properly sized opcache.memory_consumption and opcache.max_accelerated_files, PHP recompiles Moodle's large codebase on every request instead of serving it from memory.

Database Query & Index Health

Large course catalogs, gradebooks, and logstore tables slow down fast without the right indexes — the mdl_logstore_standard_log table in particular grows unbounded and needs attention.

MUC Caching (Redis/Memcached)

Moodle Universal Cache (MUC) defaults to file-based caching, which doesn't scale across multiple web servers. Application and session caches belong in Redis or Memcached once you're load-balanced.

Moodle Universal Cache: Getting It Off the Filesystem

MUC is Moodle's built-in caching abstraction, and the single highest-leverage place to tune performance once a site outgrows a single small server.

MUC Store Selection

Site Administration > Plugins > Caching > Configuration lets you assign a Redis or Memcached store to each cache definition — application, session, and request layers can each use a different store.

Redis for Application & Session Cache

Moving the application cache and session store off the filesystem and onto Redis is usually the single highest-impact change for a multi-server or high-concurrency Moodle install.

$CFG->cachejs and JS/CSS Caching

$CFG->cachejs (enabled by default) controls whether Moodle's combined JavaScript is cached and served with far-future expiry headers — leaving it off in production forces browsers to refetch on every load.

Theme & Language String Caching

Compiled SCSS and language string caches avoid recompiling theme CSS or rebuilding string tables on every request; both are purged automatically on theme or language pack changes but should stay warm otherwise.

Query Optimization & Indexing for MySQL/MariaDB

Caching hides a lot of database inefficiency, but not all of it — the underlying schema and query patterns still matter as data volume grows.

Index the Right Tables

Tables like mdl_logstore_standard_log, mdl_user_enrolments, and mdl_grade_grades are the ones queried most under real usage — missing indexes here show up directly as slow page loads at scale.

Prune or Partition the Log Store

The standard logstore captures every user action and grows continuously. Setting a log retention policy (Site Administration > Reports > Logs) or moving to an external logstore keeps the table from degrading query performance over time.

Read Replicas for Large Installs

Moodle supports configuring a read-only database replica ($CFG->dbreadonly) so reporting and read-heavy queries don't compete with transactional writes on the primary database.

InnoDB Buffer Pool Sizing (MySQL/MariaDB)

innodb_buffer_pool_size needs to be large enough to hold your working data set in memory — undersized buffer pools force constant disk reads for data that should be cached.

Cron, CDN, and Reverse Proxy Configuration

The Cron Job

Moodle's cron.php drives scheduled tasks — enrolment sync, notifications, cache cleanup, and more. Running it too infrequently backs up queued tasks; running heavy ad-hoc tasks during peak hours competes with user traffic for the same PHP-FPM workers.

CDN for Static Assets

Offloading theme CSS, JS bundles, and course files to a CDN cuts origin server load and improves load times for geographically distributed learners.

Reverse Proxy / Varnish for Anonymous Pages

Public-facing pages (course catalogs, login pages) can be cached at a reverse proxy layer, reducing the number of requests that ever reach PHP-FPM at all.

Session Handling at Scale

File-based PHP sessions don't work cleanly across multiple web servers behind a load balancer. Centralizing sessions in Redis (via $CFG->session_handler_class) keeps users logged in consistently regardless of which server handles a request.

Large Courses, Big Cohorts, and What to Watch

  • Large Cohorts in a Single Course: Courses with thousands of enrolled users stress the gradebook, participants list, and group/grouping features — pagination and report caching settings matter more as cohort size grows.
  • Big Course Catalogs: Sites with thousands of courses need the course search and category browsing indexes tuned, or category pages become the slowest thing on the site.
  • Concurrent Quiz Attempts: Scheduled exams with hundreds of simultaneous quiz attempts are one of the heaviest concurrent-write scenarios Moodle sees — this is where PHP-FPM pool sizing and database connection limits get tested hardest.
  • The Moodle Performance Report: Site Administration > Reports > Performance overview surfaces real request timing, memory usage, and cache hit rates directly from your own site — the first place to look before guessing at what to tune.

Performance Tuning Grounded in Real Moodle Configuration

Diagnosed With Moodle's Own Reports

We start from your site's actual Performance overview report and slow query logs, not a generic checklist.

Server & Application Layer, Both Covered

PHP-FPM, opcache, and database tuning alongside Moodle-level cache and cron configuration — not just one half of the stack.

Tested Under Real Concurrency

Changes verified against realistic concurrent-user and quiz-attempt scenarios before they touch production.

8+ Years Building on Moodle

Tuning informed by having watched what actually slows down Moodle installs at scale, not just documentation.

Got Questions About Moodle Performance?

Straight answers to what comes up most in a performance audit.

In most audits we run, it's caching — specifically moving MUC's application and session cache off the default file store and onto Redis. The second most common win is a properly sized PHP opcache, since Moodle's codebase is large enough that recompiling it per-request is expensive.

Start with Site Administration > Reports > Performance overview, which shows real request timing and cache statistics from your own traffic. From there, slow query logs on the database and PHP-FPM's slow log narrow down whether the bottleneck is database, PHP execution, or caching.

Yes. Moodle's cache API (MUC) includes Redis and Memcached store plugins that can be configured for any cache definition — application, session, or request-level — through Site Administration > Plugins > Caching > Configuration.

Yes, mdl_logstore_standard_log grows with every user action and, left unmanaged, becomes one of the largest tables in the database. Setting a log retention period or moving to an alternate logstore keeps queries against it from slowing down.

Yes. Static assets (theme CSS/JS, course files) are commonly served through a CDN, and anonymous-accessible pages can be cached at a reverse proxy or Varnish layer in front of Moodle to reduce origin load.

For large installs with heavy reporting load, yes — Moodle supports a read-only database connection so reports and analytics queries run against a replica instead of competing with transactional writes on the primary database.

Cron drives Moodle's scheduled tasks — enrolment sync, notifications, cache cleanup, adhoc tasks. Running it too infrequently causes task backlogs; running it too frequently or with too many heavy tasks during peak hours competes with real user requests for the same PHP-FPM workers. Tuning cron frequency and task scheduling windows matters as much as server-level tuning.

Ready to Fix a Slow Moodle Site?

Tell us about your install size and traffic and we'll scope a performance review.

Get a Free Performance Review