Technology

Moodle and LMS Hosting Performance Guide: PHP, Database and Caching Settings

If you run Moodle or another PHP-based LMS, you quickly learn that raw CPU and RAM are only half the story. Two platforms with the same server specs can feel completely different: one is smooth during peak exams, the other struggles with logins, quiz attempts and reports. The missing piece is usually how PHP, the database and caching are configured on your hosting. In this guide, we will walk through the exact server-side settings we tune on Moodle workloads at dchost.com, and how you can apply the same logic on your own hosting plan, VPS or dedicated server. We will focus on practical parameters: which PHP version and FPM options matter, which MySQL/MariaDB settings actually move the needle, and how to configure Redis or Memcached caching so that sessions and application data stay fast and reliable. Whether you are a school with 300 students or a university-scale deployment, the principles below will help you squeeze consistent, predictable performance out of your Moodle hosting.

Choosing the Right Hosting Architecture for Moodle

Before you touch any PHP or database setting, it helps to be clear about your hosting architecture. Moodle can technically run on very small shared hosting accounts, but its performance profile is closer to a serious web application than a simple brochure site.

Single-server setup (small to medium Moodle sites)

For smaller institutions, training portals or corporate LMS deployments with up to a few hundred concurrent users, a single server is usually enough:

  • Web server (Apache or Nginx)
  • PHP-FPM
  • MySQL/MariaDB database
  • Redis or Memcached for sessions and caches (optional but strongly recommended)

In this model, all services run on one VPS or dedicated server. Performance depends heavily on good PHP-FPM tuning, a correctly sized database buffer pool and fast storage (preferably NVMe). If you are comparing storage options, our NVMe VPS hosting guide explaining where speed really comes from is a good background read.

Separated database and cache (growing Moodle deployments)

Once you move beyond a few hundred concurrent learners, or run many parallel quizzes and exams, database contention becomes a bottleneck. At that point, separating services starts to pay off:

  • One server for web + PHP-FPM
  • One server for MySQL/MariaDB
  • Optional: a separate server for Redis/Memcached

This mirrors the architectures we describe for other heavy PHP apps, such as in our article on when it makes sense to separate database and application servers. Moodle benefits in similar ways: fewer context switches, better use of RAM for InnoDB caches, and clearer capacity planning for each layer.

High-availability and clustering (large universities and public LMS)

For multi-thousand concurrent users and mission-critical LMS portals, you may eventually move to:

  • Multiple web + PHP nodes behind a load balancer
  • A replicated or clustered database (e.g. primary/replica)
  • Highly-available Redis cluster for sessions and application cache

The tuning concepts in this guide still apply; you just apply them per node or per cluster. If you are estimating CPU, RAM and disk throughput, the methodology we explain in how we choose VPS specs for PHP apps like WooCommerce and Laravel also works very well for Moodle.

PHP Settings That Make Moodle Feel Fast

Moodle is written in PHP and is relatively heavy: many files, lots of database queries and often complex plugins. Optimising PHP execution is one of the easiest wins.

Use a modern PHP version and FPM

Moodle core keeps improving its support for newer PHP versions. As of recent releases, PHP 8.x is typically recommended. Newer versions bring:

  • Better performance: each request uses fewer CPU cycles
  • Improved type checking and error reporting, which helps stability
  • New language features that some plugins may rely on

On the hosting side we almost always run Moodle with PHP-FPM instead of old-style mod_php. PHP-FPM lets you tune worker pools per site and isolate noisy tenants. If you want a deeper dive into PHP 8.x and FPM pool tuning, our article on the PHP 8.x upgrade checklist and FPM pool settings lays out patterns you can adapt for Moodle.

Key PHP.ini limits for Moodle

Moodle deals with large file uploads (assignments, SCORM packages, videos), long-running scripts (backups, reports) and plenty of form submissions. Review at least these settings:

  • memory_limit: For production Moodle, 256M is usually a minimum; 512M is safer for large sites or heavy plugins. Monitor PHP fatal errors in logs to check if you hit the limit.
  • upload_max_filesize and post_max_size: Set these to accommodate your largest expected file uploads (e.g. 64M, 128M, 512M). Remember post_max_size must be ≥ upload_max_filesize.
  • max_execution_time: For interactive pages, 60 seconds is enough. For CLI cron and scheduled tasks, you can use a higher value (or rely on CLI defaults).
  • max_input_vars: Some Moodle forms post thousands of variables (question banks, permissions). 1000 (the default) can be too low; values like 3000–5000 are common on busy sites.

PHP-FPM pool tuning

On a dedicated or VPS server with only Moodle, using pm = dynamic works well. For example, on a 4 vCPU / 8 GB RAM server:

  • pm.max_children: 20–30 as a starting point
  • pm.start_servers: 4–6
  • pm.min_spare_servers: 4
  • pm.max_spare_servers: 10

The right numbers depend on your average PHP memory usage per request. A common technique is:

  1. Check average RSS memory of php-fpm processes under load (using tools like top or ps).
  2. Reserve some RAM for the OS, database and caches.
  3. Divide the remaining RAM by average PHP process size to estimate a safe pm.max_children.

If PHP-FPM processes are constantly at their max and new connections queue up, users will feel slowness during peak hours, even if CPU isn’t fully used.

Enable and tune OPcache

Moodle’s codebase is large; without OPcache, PHP re-parses hundreds of files on each request. OPcache is mandatory for serious Moodle performance. Typical settings:

  • opcache.enable = 1 and opcache.enable_cli = 1
  • opcache.memory_consumption: 256–512 MB for large Moodle instances with many plugins
  • opcache.max_accelerated_files: 20000–40000 depending on plugin count
  • opcache.validate_timestamps: 1 in development, can be 0 in production if you clear OPcache on deploy

When OPcache is too small, it starts evicting scripts and Moodle feels randomly slow under load. Monitoring OPcache hit rates and restart counts can quickly reveal undersizing.

Database Configuration for Moodle: MySQL/MariaDB Tuning

Most Moodle installations run on MySQL or MariaDB. Moodle is write-heavy (logs, quiz attempts, tracking) and benefits a lot from InnoDB tuning. The database layer is often the biggest performance gain after PHP caching.

Core InnoDB settings

On a dedicated database server (or a VPS where MySQL is the main service), start by sizing the InnoDB buffer pool correctly:

  • innodb_buffer_pool_size: 50–70% of total RAM on a database-only server, or less if the database shares RAM with PHP and other services.
  • innodb_buffer_pool_instances: 1 for small pools (< 1 GB), 2–8 for larger pools to reduce contention.
  • innodb_log_file_size: Larger files (512M–2G) reduce checkpoints for write-heavy workloads like Moodle logging, but must be balanced with recovery time.

We use very similar principles when tuning e-commerce databases; you can see that approach in our article on MySQL/InnoDB tuning for WooCommerce. The queries differ, but the idea is the same: keep hot data in RAM and avoid disk thrashing.

Connection and thread settings

Moodle can use many concurrent PHP workers, and therefore many database connections. Poorly tuned connection limits mean either lockups (too many threads) or errors (too few connections).

  • max_connections: Set just above your maximum expected concurrent PHP-FPM children across all web servers. For a single node with 30 children, 100–150 is a safe value.
  • thread_cache_size: 50–100 to reuse threads instead of creating new ones for each connection.
  • wait_timeout: Reduce from default (often 28800s) to something like 300–600s to avoid lingering idle connections.

Monitor the ratio of aborted connections and threads_created to see if your thread_cache_size and max_connections are appropriate.

Disable old query cache and use proper indexes

If you are running older MySQL versions that still have the classic query_cache, it is generally best to disable it for Moodle:

  • query_cache_type = 0
  • query_cache_size = 0

Query cache can become a global contention point on write-heavy systems like Moodle. InnoDB buffer pool plus application-level caching (Redis/Memcached) are far more effective.

Indexes are mostly handled by Moodle core, but custom reports or third‑party plugins sometimes create slow, unindexed queries. Enable the slow query log and review it regularly, just as we recommend in MySQL-heavy setups for other apps.

Storage and I/O considerations

Moodle generates a lot of small writes (logs, sessions, tracking). Using fast SSD or NVMe storage dramatically reduces I/O wait. If you are planning a new deployment, consider:

  • Storing database data and logs on NVMe or high-performance SSD
  • Separating database volume from Moodledata storage when possible
  • Monitoring iowait and disk latency under load tests

We go into more detail on measuring IOPS and disk latency in our checklist for benchmarking CPU, disk and network performance on a new VPS. The same approach helps validate whether your Moodle database volume can handle exam day.

Caching Strategy for Moodle: Sessions, Application Cache and Reverse Proxy

Moodle has its own caching subsystem (MUC) and supports multiple backends. Getting caching right can transform the user experience, especially during peak usage and for pages that repeat similar queries.

Separate session storage from database

By default, some installations use the database for PHP sessions. This quickly becomes a bottleneck under load. A better pattern is:

  • Use Redis or Memcached for PHP sessions
  • Use Redis again for Moodle’s application cache (MUC), but in a separate database / namespace

Redis tends to be the first choice because it supports persistence options and advanced data types, but Memcached is also solid for pure caching. We compare these tradeoffs in detail for another PHP CMS in our article Redis vs Memcached for object caching, TTLs and eviction tuning; the same logic applies very well to Moodle.

Configuring Redis for Moodle

At a high level, a Moodle + Redis setup typically looks like this:

  • Install Redis server on the same host or a separate cache server
  • Configure PHP session handler to use Redis (via php.ini or FPM pool settings)
  • Configure Moodle’s cache stores to use Redis for Application and Session caches

Redis settings to review:

  • maxmemory: Set based on available RAM; avoid hitting the OS OOM killer
  • maxmemory-policy: Use allkeys-lru or volatile-lru for pure cache stores; be more conservative if you also store sessions
  • Persistence: For pure caches, you can disable AOF/RDB; for sessions, most admins keep at least RDB snapshots

For large Moodle sites, high-availability Redis (with Sentinel or cluster) makes sense, similar to how we design high-availability cache setups for other CMS platforms.

Moodle’s internal cache definitions

Within Moodle’s admin interface, you can map different cache definitions to different stores. Common patterns:

  • Use file cache only for small, low-frequency definitions
  • Use Redis/Memcached for high-traffic definitions like language strings, config and core data
  • Keep cacheloader and session caches on fast in-memory stores

After setting up cache stores, use Moodle’s built-in cache performance diagnostics to check hit rates. If a definition is still hitting the database too often, consider remapping it to a faster store.

Reverse proxy and microcaching

Moodle is more dynamic than a blog, but many pages can still benefit from very short full-page caching when the same content is accessed repeatedly (course pages, resource views for anonymous or guest users). Using Nginx or another reverse proxy in front of PHP can offload a large percentage of requests.

A technique we often use for PHP apps is microcaching: caching full responses for 1–5 seconds for anonymous or low‑change pages. That may sound tiny, but at scale it can cut PHP load dramatically. We explain this pattern in more depth in our Nginx microcaching article; for Moodle you must be more careful with logged‑in users, but there are still safe opportunities, especially for static assets and public content.

Web Server and PHP–DB Interaction Details

Beyond the big blocks (PHP, DB, caching), a few web‑server and integration details make Moodle more predictable under load.

Keep-Alive and connection reuse

Ensure HTTP keep‑alive is enabled so browsers can reuse connections for CSS, JS and images. This reduces CPU and improves perceived speed. Typical Nginx settings:

  • keepalive_timeout: 10–20s
  • keepalive_requests: 100–500

On Apache with event or worker MPM, also tune the number of workers and MaxRequestWorkers so that concurrent clients don’t exhaust resources during peak sessions.

Static content offload and compression

Moodle serves many CSS/JS files and course resources. Offloading static content to a CDN or at least enabling aggressive browser caching reduces server load:

  • Serve static assets with Cache-Control headers and long TTLs, using asset versioning
  • Enable Brotli or gzip compression for text content (HTML, CSS, JS)
  • Store large file areas on fast storage or object storage when appropriate

For a broader look at how CDNs and caching affect traffic and cost, you can review our guide on controlling CDN bandwidth costs via cache hit ratio and origin pull; the same levers apply to LMS traffic.

Cron and scheduled tasks

Moodle relies heavily on cron for background work: sending notifications, cleaning up temp data, running analytics and more. Poorly tuned cron can clash with real users:

  • Run admin/cli/cron.php via system cron or a scheduler, not via web-initiated cron
  • Run it every minute or at least every 5 minutes on active sites
  • Monitor runtime; if cron regularly overlaps itself, consider splitting tasks across multiple workers or scaling resources

Offloading cron to CLI also avoids web timeouts and makes resource usage more predictable.

Monitoring, Capacity Planning and Practical Checklists

All of these settings only work if you can see how your Moodle behaves in real life. A few lightweight monitoring practices go a long way.

Key metrics to watch

  • CPU usage per core: Are PHP or DB saturating a single vCPU?
  • RAM usage: Are you swapping during exams?
  • Disk I/O and iowait: Are database writes competing for disk time?
  • PHP-FPM status: Queue length, idle vs busy workers, slow logs
  • Database metrics: InnoDB buffer pool hit ratio, slow queries, lock waits
  • Redis/Memcached stats: cache hit rate, eviction counts

Even simple tools like top, vmstat, mysqltuner and Redis’ INFO can show where the real bottleneck is before you start throwing more hardware at the problem.

Practical pre‑exam checklist

Many Moodle environments have a predictable peak: midterms, finals or company‑wide training days. In the weeks leading up to a peak event, run through a short checklist:

  • Verify OPcache is enabled and not constantly restarting
  • Confirm Redis/Memcached is used for sessions and key Moodle caches
  • Review innodb_buffer_pool_size and ensure the buffer pool hit ratio is high
  • Load test the main workflows (login, quiz start, quiz submit, reports)
  • Check PHP-FPM slow logs for any scripts consistently taking too long

If you are unsure whether your existing plan has enough headroom, the signals we describe in our guide to server‑side signs it’s time to upgrade your hosting plan map very closely to Moodle behaviour: persistent high CPU, long TTFB and frequent resource limit errors.

Bringing It All Together: A Calm Path to a Fast Moodle

Optimising Moodle hosting performance is less about chasing exotic tweaks and more about getting the basics right and keeping them consistent. Modern PHP with a tuned FPM pool, a healthy OPcache and sensible PHP limits give each request a fair chance. A well‑sized InnoDB buffer pool, realistic connection limits and a watchful eye on slow queries keep the database from becoming the bottleneck every time a class starts a quiz. Redis or Memcached take pressure off both PHP and MySQL, while short‑TTL microcaching and static asset offload ensure your web server isn’t re‑doing unnecessary work.

At dchost.com we see these same patterns across many PHP applications, and Moodle fits neatly into them. If you are planning a new LMS, consolidating several Moodle instances, or simply want to stabilise an overloaded server before the next exam season, start with the settings in this guide and then measure. Small, targeted changes often bring surprisingly big gains once the underlying architecture is sound. And if you need a hosting environment—whether shared, VPS, dedicated or colocation—that can grow with your LMS and give you room for proper PHP, database and caching tuning, our team is ready to help you design and operate a stack that keeps your Moodle fast, predictable and calm under real‑world load.

Frequently Asked Questions

The answer depends mostly on concurrent users, not total registered accounts. For small schools or training portals with up to 50–100 concurrent users, a VPS with 2–4 vCPU, 4–8 GB RAM and fast SSD/NVMe storage is usually sufficient when PHP, database and caching are tuned well. For 200–500 concurrent users, we often recommend 4–8 vCPU and 8–16 GB RAM, possibly with a separate database server. Above that, moving to multiple web nodes, a dedicated database server and a Redis cache becomes important. Always load test your typical workflows (login, quizzes, reports) rather than sizing only from user counts on paper.

Both Redis and Memcached can work well for Moodle, but Redis is often the better default. Redis supports persistence and more advanced data structures, so you can safely store sessions and multiple Moodle cache definitions with fine-grained control over TTL and eviction. Memcached is very fast for pure in-memory caching but has no persistence, so losing the process means losing all sessions. Many Moodle admins therefore use Redis for both PHP sessions and Moodle’s application cache stores, keeping performance high while reducing the risk of user logouts or unexpected cache loss.

For Moodle, three groups of PHP settings matter most. First, use a modern PHP version (typically PHP 8.x) and enable OPcache with enough memory (often 256–512 MB) and a large max_accelerated_files value to cover Moodle core and plugins. Second, tune resource limits: memory_limit (256–512 MB), upload_max_filesize and post_max_size for your largest uploads, and max_input_vars (3000–5000) to handle big forms. Third, configure PHP-FPM pools correctly: pm.max_children sized according to your RAM and average process size, and reasonable start/min/max spare servers so workers are ready during peak times without exhausting memory.

Focus on InnoDB and connection handling. On a database-focused server, assign 50–70% of RAM to innodb_buffer_pool_size so that hot Moodle tables stay in memory and disk reads are minimised. Use multiple buffer pool instances for larger pools to reduce contention. Increase innodb_log_file_size (for example 512M–2G) to smooth checkpoints in write-heavy periods such as quizzes. Set max_connections slightly above your total PHP-FPM workers and use a reasonable thread_cache_size so connections can be reused. Disable the old query cache and instead rely on InnoDB and Redis/Memcached. Finally, enable the slow query log and review it regularly to catch plugin or report queries that need indexing.

You can get acceptable performance on small to medium Moodle sites with good PHP, database and Redis tuning alone, but a CDN and/or reverse proxy cache can significantly reduce server load and improve perceived speed. A CDN is ideal for static assets like CSS, JS and media files used in courses, cutting latency for geographically distributed users. A reverse proxy like Nginx can provide microcaching (1–5 seconds) for certain low-change pages and aggressively cache static content, offloading many requests from PHP. For large or public Moodle portals, combining strong backend tuning with smart edge caching gives you the most headroom during peak exam traffic.