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.
İçindekiler
- 1 Choosing the Right Hosting Architecture for Moodle
- 2 PHP Settings That Make Moodle Feel Fast
- 3 Database Configuration for Moodle: MySQL/MariaDB Tuning
- 4 Caching Strategy for Moodle: Sessions, Application Cache and Reverse Proxy
- 5 Web Server and PHP–DB Interaction Details
- 6 Monitoring, Capacity Planning and Practical Checklists
- 7 Bringing It All Together: A Calm Path to a Fast Moodle
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:
- Check average RSS memory of php-fpm processes under load (using tools like
toporps). - Reserve some RAM for the OS, database and caches.
- 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.
