Slow Moodle: Real Causes and How to Diagnose
Before upgrading your Moodle server, measure. The most common causes of slowness are configuration issues—and cost nothing to fix.
by Cleverson Gouvêa
Slow Moodle is rarely due to an undersized server. In most of the support calls I handle, the slowness comes from three things that cost nothing to fix: disabled cache, delayed cron jobs, and an untuned database. This guide shows how to diagnose in 30 minutes, what metrics to ask for before signing a larger contract, and how to recognize when changing servers is, in fact, the right decision.
TL;DR
- Slow Moodle is a symptom, not a diagnosis. Measure TTFB, page generation time, and number of queries per page before anything else.
- The most common causes are configuration issues: theme designer mode enabled, MUC and sessions on disk, missing OPcache, hourly cron jobs, and a bloated log table.
- The Performance overview report (Site administration → Reports) identifies three of these issues in 10 seconds.
- Upgrading your server only helps when the bottleneck is actual CPU, RAM, or I/O saturation—and this should be proven with metrics, not gut feeling.
- Moodle 5.1 (10/06/2025) requires PHP 8.2+ and changed the document root to
/public; Moodle 5.2 was released on 04/20/2026, and the next LTS, 5.3, is planned for 10/05/2026.
I've worked with Moodle since 2008 and have operated environments with tens of thousands of students during enrollment peaks and exam weeks. Almost every time a client comes to me saying they want a bigger server, the problem lies in the software layer. And when the server truly is the bottleneck, it can be proven in minutes—with data, not guesswork.
Slow Moodle: What True Slowness Is and How to Measure It
The first pitfall is semantic. The expression 'slow Moodle' is used to describe at least five different problems, each with a distinct cause:
- Page takes a long time to start loading — the server is thinking too much. This is TTFB (Time To First Byte), and it points to PHP, database, or cache issues.
- Page loads, but appears somewhat broken and slow to respond to clicks — this is a front-end issue: JavaScript and CSS being served without caching, compression, or a CDN.
- Only a specific course page is slow — this is content-related: a course with hundreds of activities, a heavy filter, or a poorly written plugin.
- It's slow only from 7 PM to 10 PM — this is concurrency. The server handles daily load but not peak load.
- It's slow during exam week — this is the quiz. The
quizmodule is the most database-intensive in Moodle.
The Three Metrics You Need Before Opening a Support Ticket
Before speaking with any vendor, collect:
- TTFB measured externally, using
curl -w "%{time_starttransfer}\n" -o /dev/null -s https://your-moodle/. Run it ten times, both during peak and off-peak hours. - Page generation time and number of queries, which Moodle itself reports in the footer when you enable performance info.
- Server CPU, RAM, and I/O usage during the same interval, using
top,vmstat 1, andiostat -x 1.
If the TTFB is 3 seconds and the CPU is at 20%, the server is not the problem. Remember that sentence—it saves entire contracts.
Application-Level Causes That Slow Down Moodle Without Hardware Fault
These are what I most frequently encounter during audits. Every slow Moodle instance that crossed my desk had at least two of these issues—and all can be resolved without changing a single piece of hardware.
Theme Designer Mode Enabled
This is the number one cause of slow Moodle on my list. With theme designer mode active, Moodle recompiles CSS and JavaScript with every request from every user. Moodle's own documentation warns that this dramatically degrades performance. Someone turns it on to adjust a color in the theme, forgets to turn it off, and the site drags for months. Check under Site administration → Appearance → Themes → Theme settings.
MUC and Sessions on Disk
Moodle uses MUC (Moodle Universal Cache), a layered caching system for course definitions, contexts, language strings, and permissions. By default, it resides on the file system. This works for small sites; however, with a few hundred concurrent users, each read becomes an I/O operation.
The same applies to sessions. Moodle's session handling documentation recommends the Redis driver and explicitly states that database sessions have poor performance and are not recommended for large sites. The configuration goes into config.php:
$CFG->session_handler_class = '\core\session\redis';
$CFG->session_redis_host = '127.0.0.1';
$CFG->session_redis_prefix = 'meusite_us_';
$CFG->session_redis_acquire_lock_timeout = 120;
$CFG->session_redis_lock_expire = 7200;
A detail almost no one knows: Moodle locks the session during the request. If a heavy page holds the lock for 20 seconds, that same student's other tabs will wait. The user swears the slow Moodle is the server's fault; in reality, they are competing with themselves.
OPcache Disabled or Undersized
The official performance recommendations documentation is direct: ensure that PHP's built-in accelerator—OPcache—is enabled. Without it, PHP reinterprets thousands of files with every click. A site with OPcache disabled is guaranteed to be a slow Moodle, even on a powerful machine: it's the adjustment with the best effort-to-gain ratio in Moodle. Check with php -i | grep opcache.enable and look at the hit rate: if opcache_get_status() shows misses constantly rising, the OPcache memory is too small for the size of your code plus plugins.
Delayed Cron Jobs or Running via HTTP
The documentation recommends running cron every minute, and always via CLI (php admin/cli/cron.php), never via an HTTP call—in large installations, HTTP-based cron consumes too much memory. A delayed cron isn't the type of slow Moodle that shows up in TTFB; it builds up a queue. Notifications, search indexing, trash cleanup, and grade processing pile up and then burst all at once. Under Site administration → Server → Scheduled tasks, you can see the last execution of each task. If tasks that should run hourly ran yesterday, you've found a problem.
Bloated Log Table and Enabled Statistics
The mdl_logstore_standard_log grows endlessly. In an active EAD environment, it can exceed tens of millions of rows in a year. Reports and the recent activity block then have to scan this mountain of data. Adjust the log lifetime under Site administration → Server → Cleanup, and disable statistics if you don't use them—Moodle's own performance report recommends exactly this.
Infrastructure Causes: When the Bottleneck is Real
Untuned Database
A MariaDB instance with default configuration operates with a tiny buffer pool. Moodle's documentation recommends that for a dedicated database server, innodb_buffer_pool_size should be around 80% of RAM, with innodb_buffer_pool_instances matching the number of CPU cores. If your database is 40 GB and the buffer pool is 128 MB, every query goes to disk. An undersized buffer pool is the most common cause of slow Moodle during peak hours, and no server is large enough to compensate for it.
It's worth remembering the minimum version requirements for Moodle 5.1: PostgreSQL 15, MySQL 8.4, or MariaDB 10.11. Running below these versions is not just a support risk—it means foregoing database-level optimizations.
Poorly Sized PHP-FPM
Too few workers create a queue: requests wait in listen.backlog, and the student sees a frozen page. Too many workers exhaust RAM, and the system starts using swap—that's when slow Moodle turns into a frozen Moodle. The calculation is simple: take the average real consumption of one worker (typically 60–120 MB in Moodle with plugins) and divide the available RAM for PHP by that value.
Disk, Network, and Assets
The installation documentation suggests 200 MB for the code plus space for content, with 5 GB as a realistic minimum, and 8 GB of RAM or more for large production servers. But the number that hurts most is IOPS. Moodle with moodledata on a slow network drive, or with a poorly configured NFS, delivers high TTFB even with an idle CPU. I've seen a 60% gain just by moving localcachedir to a fast local disk.
30-Minute Slow Moodle Diagnosis Guide
Follow this order. Each step eliminates a hypothesis.
Step 1 — Open the Performance Report
Site administration → Reports → Performance overview. This checks, among other items, if theme designer mode is enabled, if JavaScript caching (cachejs) is active, and if statistics are being recorded. Three clicks, three causes eliminated.
Step 2 — Enable Performance Info
Under Site administration → Development → Debugging, enable Performance info. The footer will then show generation time, memory, and cache hit/miss. To count queries, add to config.php:
define('MDL_PERF', true);
define('MDL_PERFDB', true);
define('MDL_PERFTOFOOT', true);
An institutional page with over 200 queries is a sign of a misbehaving plugin or block. A course page with 1,500 queries is a bug, not a lack of server capacity.
Step 3 — Log Slow Queries
Still in config.php, Moodle allows logging queries above a threshold in the mdl_log_queries table, via dboptions with logslow. Run it for a full day and look at the ten worst. It's usually the same query, from the same plugin.
Step 4 — Correlate with Time
Cross-reference the TTFB with the CPU and database connection graphs. Slow Moodle only at peak times is a sizing issue; slow Moodle all the time is a configuration issue. This is the most important distinction in the entire diagnosis.
Table of Symptom, Probable Cause, and Where to Look
| Symptom | Probable Cause | Where to Check | Typical Fix |
|---|---|---|---|
| Slow all day, low CPU | Theme designer mode / OPcache off | Performance overview; php -i |
Disable designer mode; enable OPcache |
| Slow only at night peak | PHP-FPM workers or database saturated | top, MariaDB connections |
Resize pool; InnoDB tuning |
| Slow after activating a plugin | Looping queries | Performance info (number of queries) | Replace or fix the plugin |
| Delayed notifications and grades | Delayed cron or via HTTP | Scheduled tasks | CLI cron every minute |
| Reports freeze the entire site | Giant mdl_logstore_standard_log |
Table size | Reduce log lifetime; read replica |
| Slow login, tabs freezing | Session lock on disk | config.php |
Sessions in Redis |
The Tuning Checklist That Solves Most Slow Moodle Cases
If I had an hour and root access, I would do exactly this, in this order:
- Disable theme designer mode and enable
cachejs. - Enable and size OPcache; review
memory_limitinphp.ini. - Move sessions to Redis and point MUC stores to Redis as well.
- Adjust
innodb_buffer_pool_sizeandinnodb_buffer_pool_instancesin MariaDB. - Set up CLI cron to run every minute, under its own user and worker.
- Reduce log lifetime and disable unused statistics.
- Place
localcachediron a fast local disk. - Publish static assets behind a CDN with compression.
On average, in the environments I've taken over, this checklist reduced TTFB from 3–4 seconds to less than 1 second—without changing a single machine. It's the same work we do during the onboarding for our managed Moodle hosting, before discussing any plan upgrades.
When Upgrading Your Server Is the Right Answer, and When It Isn't
Upgrading makes sense when, with the checklist above already applied, you observe:
- Sustained CPU above 80% at peak, with an execution queue (
load average) exceeding the number of cores; - RAM saturated with active swap—swap on a database server is a sentence to slowness;
- Double-digit I/O wait in
iostat; - Database and web server competing on the same machine, with the buffer pool already at the limit of what RAM allows;
- Projected growth that you can forecast: doubling students in the next semester, for example.
Don't upgrade when: the problem appears only on one page, only after installing a plugin, only during exam week (then it's the quiz and cron), or when no one has measured anything. A bigger server with disabled cache will still be a slow Moodle—just a more expensive one. I've had clients who tripled their monthly infrastructure cost and gained only 8% TTFB improvement because the bottleneck was mdl_log with 90 million rows.
It's worth comparing with the numbers the community itself uses as a reference for scale: the development documentation cites installations reaching 1 million users, 50,000 courses, and 5,000 users per course. If your Moodle has 3,000 students and is slow, the problem is almost certainly not capacity.
Migration Pitfalls and Hidden Costs
PHP Version. Moodle 5.1 requires PHP 8.2 minimum, and accepts 8.3 and 8.4, only in 64-bit, with the sodium extension and max_input_vars of at least 5000. Migrating to a server with an old PHP version is migrating to a dead end.
The /public change. Starting with Moodle 5.1, there is a /public directory, and the web server's document root must point to it—no longer to the Moodle root. Those who migrate by copying the old vhost will break the site or expose files that should not be accessible. Plugins installed before the upgrade remain in the old location and must be moved manually.
Version Cycle. Moodle 4.5 LTS had general support end on 10/06/2025 and maintains security support only until 10/04/2027. 5.2 arrived on 04/20/2026, and the next LTS, 5.3, is planned for 10/05/2026. Plan server migration together with version upgrades—doing them separately costs two maintenance windows and two risk periods. I wrote about this calculation in the post on version, LTS, and AVA upgrade at UFBA, and the case of the PVANet update at UFV to Moodle 4.5 shows the real effort involved in a public institution.
Cost of Living with Slow Moodle. Each semester delaying diagnosis leads to student attrition, academic support calls, and instructors migrating content outside the LMS. This cost doesn't appear on the infrastructure spreadsheet, but it's the largest of all.
Hidden Cost of Downtime. A poorly planned maintenance window during exam week costs more than a year's difference in hosting plans. The access pattern of public institutions, with peak concentration at the beginning of the semester, is well illustrated in my analysis of the UTFPR Moodle access peak in 2026/2.
How Agathas Web Solves This
Our managed Moodle hosting starts precisely with the diagnosis described above—and the initial diagnosis is free, even if you decide to stay where you are.
What's included:
- Moodle-tuned Stack, not generic hosting: PHP-FPM with a dedicated pool per instance and optimized OPcache, Redis for sessions and MUC, MariaDB 10.11+ with binlog, Nginx, and isolated cron jobs on their own workers.
- 24/7 Monitoring with Grafana, Sentry, and external uptime verification, with alerts to the technical team before students complain.
- Daily Backup with 15-minute Incremental Binlog, encrypted offsite storage, and monthly tested restores.
- Assisted Migration within 5 Business Days, with parity for users, courses, grades, badges, and forums, staging validation, cut-over during a nighttime or weekend window, and the old environment preserved for 30 days for rollback.
- Contractual 99.9% Uptime SLA, response within 15 minutes for critical incidents, and senior support in Portuguese, via WhatsApp, without Level 1 agents reading scripts.
- Monthly Report with resource usage, critical events, tested backups, and suggested improvements.
How it works in practice: You grant us read-only access to your current environment, we run the diagnosis, and we provide a document outlining the bottleneck, the expected gain from each correction, and the appropriate sizing. If your current server can handle it after tuning, we'll tell you. If not, the migration will already be quoted.
How to contract: Through the form on the managed Moodle hosting page or via the technical team's WhatsApp. Monthly contracts, no long-term commitment, with 30-day cancellation and data return in standard format.
Conclusion: Diagnose, Then Decide
Slow Moodle is a symptom, not a diagnosis. Measure TTFB, count queries, check scheduled tasks, and run the performance overview report before making any infrastructure decisions. In most cases, the gains come from configuration—and they are significant. When the bottleneck truly is capacity, you'll have data to justify the upgrade, not just a feeling.
If you want to start with a diagnosis, it's the first step in our managed Moodle hosting: we identify the bottlenecks in your current environment and propose corrections before any contract.
Related posts

Moodle Hosting: Requirements, Costs, and Pitfalls in 2026
PHP 8.3, minute-by-minute cron, and moodledata outside the public folder: discover what separates a Moodle hosting plan that survives exam week from one that crashes.
Migrate Moodle to a New Server: No Data Loss, No Downtime
A blueprint for LMS operators: the three critical components, the pre-migration rehearsal, and a cutover window measured in minutes.
Moodle on AWS vs. Managed Hosting: The True Cost
Your EC2 bill is just the starting point, not the total price. Egress, NAT Gateway, and staff time are the real cost drivers — and often overlooked.