How to Choose Hosting for Moodle: Load, CPU, RAM, PHP-FPM, and Cron
Two Moodle sites can each have 1,000 registered students and still require completely different servers. On one site, only 20–30 people may be active at the same time during the day. On another, 200 users may log in within a minute and start the same quiz. The number of accounts is identical; the load on PHP, the database, and storage is not.
That is why choosing hosting for Moodle only by figures such as “4 vCPU, 8 GB RAM, 100 GB NVMe” is risky. Start with the learning scenario, expected peak concurrency, and the limits of the hosting plan. Only then does it make sense to compare processors, memory, and storage types.
A useful Moodle hosting check follows this chain: compatibility with the selected Moodle version, CPU and RAM, PHP-FPM, database, cron, moodledata, disk I/O, caching, backups, and a realistic load test. This takes longer than choosing a plan from a specification table, but it gives a much better answer to the question that actually matters: will the platform survive the next exam or large class session?
How many Moodle users will actually generate load at the same time?
Moodle server sizing should be based on peak concurrent activity, not on the total number of accounts. A site with 1,000 registered students may remain lightly loaded most of the day, while a group of 150–200 users can create a sharp spike if everyone logs in, opens a quiz, and submits answers at nearly the same time.
So the statement “we have 500 students” is not enough to choose a hosting plan. It helps estimate database growth and storage, but it says very little about the instantaneous load on PHP and the database.
Why 1,000 students do not mean 1,000 simultaneous requests
A user may be shown as online without sending a PHP request every second. A student can be reading a page, watching embedded video, typing into a field, or simply leaving the browser tab open. During that time, the server may do almost nothing for that user.
The situation changes during synchronized actions. A typical exam scenario looks different: the teacher opens access at 10:00, a whole group logs in within a few minutes, opens the test, loads questions, saves answers, and moves between pages. PHP requests, database activity, session operations, and background tasks all rise within a short period.
| Scenario | What happens on the server | What to check |
|---|---|---|
| Reading course materials | Relatively light PHP and database activity between periods of reading | Response time, PHP-FPM, cache |
| Mass login | Short burst of authentication and session activity | PHP workers, DB, Redis sessions if used |
| Quiz or exam | Many parallel requests, state changes, and answer submissions | CPU, PHP-FPM, DB latency, HTTP errors |
| Assignment uploads | PHP processing plus writes to moodledata |
PHP limits, disk I/O, free space |
| Heavy reports | Long database queries and CPU load | DB load, slow queries, response time |
Before comparing hosting plans, write down a simple workload profile:
- total number of registered users;
- typical number of active users during the day;
- expected peak concurrent users;
- whether exams start at the same scheduled time;
- whether the site uses quizzes, H5P, SCORM, or heavy reports;
- how many users may upload files at the same time;
- whether the platform contains a large video library;
- which hours are likely to produce the heaviest load.
If Moodle is already running, replace assumptions with measurements. Compare the class schedule with Moodle logs, the web server access log, active PHP-FPM processes, CPU, RAM, database connections, and response time. The daily average is less useful than the server state at the exact moment users report delays.
A useful sizing description: instead of “we have 1,200 students,” say “normal concurrency is a few dozen users, while exams may produce up to 180 participants starting the same quiz at once.”
Once this workload profile exists, CPU and RAM discussions become meaningful. Without it, a table that says “N students = N GB of RAM” only creates the appearance of precision.
Does the hosting support your Moodle version?
Hosting is suitable for Moodle only if it meets the requirements of the exact Moodle branch you plan to use. A generic “PHP + MySQL supported” label does not guarantee compatibility.
For example, Moodle 5.2 requires at least PHP 8.3, supports PHP 8.4, requires the sodium extension, expects max_input_vars to be at least 5000, and requires 64-bit PHP. Minimum supported database versions for that branch are also relatively recent: PostgreSQL 16, MySQL 8.4, MariaDB 10.11, or Microsoft SQL Server 2019. Other Moodle branches have different requirements, so always check the documentation for the release you actually intend to run.
Minimum software compatibility and a good production configuration are two different things. Compatibility means Moodle can run on the platform. It does not mean the hosting plan can handle your workload.
| Parameter | Where to check | Why it matters | What to ask the provider |
|---|---|---|---|
| PHP version | php -v |
Compatibility with the selected Moodle version | Can the PHP version be changed? |
| PHP extensions | php -m |
Missing required modules can block installation or features | Can required extensions be enabled? |
| Database version | SELECT VERSION(); |
The DBMS must meet Moodle requirements | Which MySQL, MariaDB, or PostgreSQL version is used? |
max_input_vars |
php -i | grep max_input_vars |
Moodle checks this PHP setting | Can the value be changed? |
memory_limit |
php -i | grep memory_limit |
Limits memory available to an individual PHP request | What is the maximum allowed value? |
| Upload limits | php -i or the PHP control panel |
Affects assignment and file uploads | What are the maximum upload_max_filesize and post_max_size values? |
| Cron | Cron panel or shell | Required for scheduled and ad hoc tasks | Can CLI cron run every minute? |
| CLI PHP | php -v over SSH |
Useful for running Moodle cron | Does CLI PHP use the required version? |
If Moodle is already installed, open Site administration → Server → Environment. A red warning in the Environment check is more useful than a marketing claim such as “Moodle compatible,” because Moodle checks the environment against the requirements of the selected version.
With SSH access, a basic check looks like this:
php -v
php -m
php -i | grep max_input_vars
php -i | grep memory_limit
For MySQL or MariaDB, check the version with:
SELECT VERSION();
On shared hosting without SSH, some of this information is usually available through a PHP Selector, the hosting control panel, or a temporary phpinfo() page. Remove such a page after checking because it exposes details about the PHP environment.
If the required PHP version, extension, or cron configuration cannot be enabled, adding more RAM will not solve the problem. That is a platform limitation, not a capacity problem.
How much CPU and RAM does Moodle need, and why does “N students = N GB” fail?
You cannot accurately size Moodle CPU and RAM from the number of students alone. Memory is shared between Linux, the web server, PHP-FPM workers, the database, Redis, and system services. CPU usage depends on how many requests run in parallel and what users are doing.
This is why simple tables such as “100 users = 2 GB, 500 users = 4 GB, 1,000 users = 8 GB” are unreliable. They ignore concurrency, plugins, PHP process size, database configuration, and workload type.
Which metrics indicate a RAM shortage?
Do not judge memory pressure only by the used memory column. Linux deliberately uses unused RAM for filesystem cache, so a high used value does not automatically mean the server is short on memory.
Check:
free -h
vmstat 1
Look at available memory, swap activity, and whether those values change during the actual load spike. If available RAM nearly disappears during an exam, swap activity becomes persistent, and response time degrades at the same time, the server is under real memory pressure.
Then identify which processes consume the memory:
ps aux --sort=-rss | head
Sometimes PHP-FPM workers are the main consumers. Sometimes it is the database or another service. Adding RAM without checking this may help temporarily, but you still will not know which limit you are actually fixing.
What does a CPU bottleneck look like?
CPU becomes a bottleneck when cores remain heavily utilized during the relevant workload, process queues grow, and PHP or the database cannot process requests quickly enough.
A quick check:
top
htop
uptime
If a four-vCPU server stays heavily loaded but MySQL is the main CPU consumer, simply increasing pm.max_children can make things worse. More PHP workers may send even more parallel queries to an already overloaded database.
The opposite also happens. CPU usage may remain moderate and RAM may still be available while Moodle remains slow. In that case, the bottleneck may be PHP worker limits, storage, or the database.
- Estimate normal and peak concurrency.
- Choose a reasonable starting configuration instead of the largest VPS “just in case.”
- Deploy Moodle with the actual plugins and configuration.
- Reproduce both typical and peak workloads.
- Check CPU, available RAM, swap, PHP-FPM, database activity, and disk I/O.
- Keep headroom and make sure the plan can be upgraded without a complex migration.
This approach gives a less convenient answer than “you need 8 GB,” but it sizes the server around an observed bottleneck rather than around the number of rows in the user database.
Why does Moodle need more than just enough disk space?
A server can have plenty of free disk space and still slow Moodle down because of storage performance. A typical example: Moodle works normally during the day, a backup starts, students become active at the same time, and response time increases sharply. df -h still looks fine because the problem is not capacity.
Storage has two separate jobs. Capacity answers “how much data fits?” I/O performance answers “how quickly can the server read and write it?”
A Moodle server may store:
- Moodle application code;
- the
moodledatadirectory; - the database, if it runs on the same server;
- cache and temporary files;
- user uploads;
- local backups.
The moodledata directory should be outside the publicly accessible web root. The web server must be able to work with its contents, but users should not be able to fetch those files directly over HTTP and bypass Moodle permissions.
How do you distinguish low disk space from slow storage?
Start by checking filesystem capacity and inode usage:
df -h
df -i
du -sh /path/to/moodledata
df -h shows free space, du estimates the size of moodledata, and df -i exposes another limit: inode availability. On hosting with very large numbers of small files, inodes can run out before disk space does. New writes may then fail even though the panel still shows free gigabytes.
For migration planning, record not only the current size of moodledata but also its monthly or semester growth. Add database size, temporary data, local backups, and operational headroom.
If capacity looks healthy, check I/O:
iostat -x 1
Do not focus on one “magic” column. Look at several values together:
r/sandw/sshow read and write activity;awaitshows average I/O completion latency;%utilcan help show device utilization, but it should not be interpreted mechanically across SSD, NVMe, RAID, and virtual disks;- storage latency should be compared with the exact time Moodle becomes slow.
A single high await value proves little. A more convincing pattern is: backup starts at 09:00, students enter at 09:02, await increases, Moodle response time increases as well, and both return to normal when the backup finishes.
In that case, the same storage is serving several competing workloads: the database is reading and writing its own files, Moodle is working with moodledata, and the backup process is reading a large amount of data. Faster PHP will not fix that.
NVMe normally provides useful latency and IOPS headroom, but it does not repair slow PHP code or a poor SQL execution plan. If a query spends five seconds in the database because of inefficient execution, faster storage may only partly reduce the problem.
Before choosing a plan, know at least five numbers: the size of moodledata, database size, inode usage, the size of one full backup, and the rate at which data is growing. Storage performance should be checked under real workload rather than inferred from the word “NVMe” in a plan description.
Can the hosting run Moodle cron every minute?
Moodle depends on regularly running cron, and the official documentation recommends running it every minute. The ability to configure that schedule should be checked before buying shared hosting or moving the site to a new server.
Failure is not always obvious. Courses may open and logins may work, so the installation appears healthy. Later, scheduled tasks begin to lag, notifications arrive late, and the ad hoc task queue grows.
On Linux, a common CLI configuration looks like this:
* * * * * /usr/bin/php /path/to/moodle/admin/cli/cron.php >/dev/null
Do not copy the paths blindly. The PHP binary and Moodle directory can be different on another server. Test the command manually first:
/usr/bin/php /path/to/moodle/admin/cli/cron.php
If you do not know the CLI PHP path:
which php
php -v
The second command also shows which PHP version the shell uses. On servers with multiple PHP versions, CLI PHP and the PHP-FPM instance serving the website may use different versions and different php.ini files.
How do you know cron is running but falling behind?
A crontab entry by itself proves nothing. In Moodle, open Site administration → Server → Tasks → Scheduled tasks and check the next run time, failures, and delays. Review ad hoc tasks and task logs as well.
If cron starts every minute but the task queue grows faster than it is processed, the schedule is no longer the main problem. Look for a slow task, insufficient CPU, database delays, locks, or limitations on parallel execution.
Another edge case appears when a new cron invocation begins every minute while the previous run is still active. That is not automatically an error because Moodle has locking mechanisms, but if cron runtimes keep growing, tasks fall behind, and the server remains busy, identify which tasks are taking the time.
During troubleshooting, keep cron stdout and stderr instead of immediately sending everything to /dev/null. Otherwise the evidence you need may disappear together with the command output.
A useful question for the provider: “Can admin/cli/cron.php run every minute through PHP CLI, and are there limits on how long the job may run?”
If a hosting plan allows cron only every 15 or 30 minutes, evaluate whether that restriction is acceptable for your Moodle setup, especially when the platform relies heavily on background tasks.
Which PHP-FPM limits can slow Moodle down before CPU or RAM runs out?
Sometimes the symptoms look contradictory: CPU is only half utilized, free RAM is still available, yet Moodle starts taking several seconds to respond during mass login. At the same time, the PHP-FPM log contains a message such as:
server reached pm.max_children setting
That is a good reason to inspect the PHP-FPM pool rather than immediately upgrading the VPS. Moodle can run out of available PHP workers before the machine runs out of RAM or reaches 100% CPU.
One of the key settings is pm.max_children. It limits the number of PHP-FPM processes that can serve requests simultaneously within a pool.
If the limit is too low, some requests run while the rest wait for a free worker. Depending on web server configuration and timeouts, users may see slow pages, 502/503 responses, or timeouts.
Setting pm.max_children to a very large value is not a safe fix either. Every worker uses memory, while the operating system, database, Redis, and other services need memory too.
Check real process memory usage:
ps aux --sort=-rss | head
free -h
For rough sizing, use the actual RSS of PHP workers under your Moodle workload rather than PHP memory_limit. memory_limit caps memory available to a PHP request; it does not tell you how much memory each worker normally occupies.
The formula RAM / memory_limit = pm.max_children is wrong. The server also needs memory for the OS, database, Redis, filesystem cache, and other processes, and real PHP worker RSS is not equal to memory_limit.
If PHP-FPM status or pool metrics are available, watch:
- active processes;
- idle processes;
- max active processes;
- listen queue;
- whether the pool reaches
max children.
How do you distinguish a PHP-FPM limit from a CPU bottleneck?
The same complaint — “Moodle is slow” — can come from very different causes. Comparing several metrics at the same time usually narrows the search quickly.
| Symptom | What to check | Likely next step |
|---|---|---|
| CPU is moderate, RAM is available, PHP-FPM queue is growing | Status page, pm.max_children, PHP-FPM log |
Check whether the pool or hosting process limit is too small |
| CPU is saturated and workers are active | top, PHP/DB process load, response time |
Investigate CPU, PHP code, or database load |
Log contains reached pm.max_children |
Compare the timestamp with the Moodle load spike | Check pool size and available RAM |
| 502/504 errors without max_children messages | Web server error log, PHP-FPM log, upstream timeout | Investigate timeout, backend failure, or a long-running request |
Timing matters. If max_children was reached once at night during an administrative task, that does not explain daytime complaints. If it appears every time a quiz begins, the evidence is much stronger.
On shared hosting, customers often cannot see pm.max_children directly. Ask support for the maximum number of concurrent PHP processes, Entry Processes, or the equivalent limit. Names differ between platforms, but the question is the same: how many dynamic requests can the account process in parallel?
A server can have free RAM while PHP requests are already waiting in a queue. For Moodle installations with sharp workload peaks, this is one reason generic CPU and RAM charts can miss the real bottleneck.
How important are the database, OPcache, and Redis for Moodle?
Database and cache performance can be just as important as the number of vCPUs. If dashboards, quizzes, or reports are slow, adding CPU before checking the database and PHP cache may not solve anything.
Moodle is database-intensive. When MySQL or MariaDB runs on the same VPS, it competes for RAM and I/O with PHP-FPM, the web server, and other services. Adding more PHP workers does not automatically increase capacity because the database may become the next bottleneck.
What should you check in the database?
You do not need to turn the first diagnostic pass into a complete MySQL tuning exercise. Start with a few questions:
- Does the number of database connections rise during the peak?
- Are slow queries accumulating?
- Does database CPU rise at the same time as Moodle response time?
- Does the database have enough memory?
- Is storage latency high?
- Is a heavy report or backup running at the same time?
For MySQL or MariaDB, begin with a few specific counters instead of dumping every variable:
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Threads_running';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
SHOW GLOBAL VARIABLES LIKE 'max_connections';
Threads_connected shows open connections, Threads_running shows how many threads are actively executing work, Slow_queries can reveal accumulation of slow queries, and max_connections shows the configured upper limit. None of these values proves a bottleneck on its own. Compare them with database CPU, response time, and disk I/O during the actual peak.
A high Threads_connected value with only a few Threads_running threads does not automatically mean the database is overloaded. If running threads, database CPU, and Moodle response time all increase together, it is time to inspect queries more deeply and review the slow query log.
Why does Moodle benefit from OPcache?
OPcache stores compiled PHP bytecode in memory so PHP does not need to parse and compile the same files on every request. For a large PHP application such as Moodle, it is a normal part of a production setup.
Check it with:
php -i | grep opcache.enable
If you run this command in CLI, remember that CLI PHP and PHP-FPM may load different configurations. For the website, verify the OPcache settings of the PHP handler that actually serves Moodle.
When is Redis useful for Moodle?
Redis can be configured as a Moodle cache store and, with the appropriate setup, can also store sessions. On a growing Moodle platform, this can move some work away from filesystem-based caching and use a fast in-memory backend instead.
Check whether a local Redis instance responds:
redis-cli ping
The expected response is:
PONG
But PONG only proves that Redis is running. It does not prove that Moodle uses it correctly. Check Moodle cache stores and mappings, and verify session configuration separately if Redis is being used for sessions.
A common edge case is that Redis is running and PING works, so the administrator assumes caching is configured, while the relevant Moodle caches are still mapped to another store. Availability and actual use are different checks.
Redis is not mandatory for every small LMS. If Moodle is fast and measurements show no cache or session bottleneck, adding another service may bring little benefit.
The reverse is also true: Redis will not rescue a server where PHP is CPU-bound and the database is overloaded. Find the bottleneck first, then change the architecture.
Should video be stored directly on the Moodle server?
A large video library does not have to be stored and delivered from the same server that runs PHP, the database, and moodledata. Video changes requirements mainly for storage, network bandwidth, and backups rather than only for Moodle CPU.
Suppose a course contains 100 videos at 500 MB each. That is already about 50 GB of source files before future uploads and backups. But capacity is only half of the question. If dozens or hundreds of students watch video at the same time, outbound bandwidth may become the real limit.
For a rough monthly traffic estimate:
average transferred data per view × number of views
This estimates total traffic volume but says little about the peak. Network sizing needs a second calculation.
How do you estimate peak bandwidth for video?
A rough bandwidth requirement can be estimated as:
average bitrate per stream × concurrent viewers
If a stream averages around 2 Mbit/s and 100 students watch simultaneously, the video payload alone is already around 200 Mbit/s. Real network usage will be somewhat higher because of protocol overhead and other site traffic.
This does not mean 100 students always require exactly that amount of bandwidth. Users may watch different videos, pause playback, receive adaptive streams at different bitrates, or receive some data from cache. The formula is for scenario sizing, not for creating a universal rule.
Do not mix up two metrics: GB or TB per month describes total transferred data, while Mbit/s or Gbit/s tells you whether the connection can handle the concurrent peak.
If video traffic is much larger than all other site traffic, the question “what VPS does Moodle need?” partly becomes “how should we deliver video to students?”
Possible architectures include:
- keeping a small amount of video locally;
- using a dedicated video platform;
- moving large files to object storage;
- using a CDN;
- embedding content from an external service when appropriate.
An external video platform or object storage can remove a large part of the storage and network load from the Moodle server, but it introduces other questions: access control, privacy, storage region, egress costs, and whether direct content links can be restricted.
If video remains local, check:
- the current media-library size;
- monthly traffic volume;
- available outbound connection speed;
- maximum concurrent viewers;
- average video bitrate;
- network throughput during lessons;
- whether video is included in backups;
- how long backup and restore take.
Sometimes this is the reason an apparently simple Moodle deployment requires an expensive server: one VPS is acting as LMS, video host, and backup archive at the same time. Those workloads can often be separated.
Which backup, restore, security, and mail features should you check before migration?
Production Moodle hosting needs to do more than survive normal load. It must also allow the platform to be restored predictably after failure. A checkbox that says “daily backup” does not answer that question.
At minimum, Moodle depends on the database, moodledata, and configuration. Restoring only an SQL dump and discovering that the referenced files are missing is not a recovery plan. The reverse — restoring files that do not match the database state — is also a problem.
| Check | What to find out | Bad sign |
|---|---|---|
| Database backup | How and how often the database is copied | The database is not included |
moodledata |
Whether the directory is included | Only the web root is copied |
| Off-server copy | Whether a backup exists outside the production VPS | All backups are stored on the same disk |
| Retention | How many restore points are kept | Only the newest copy exists |
| Restore | Whether the system can be restored and how long it takes | Backups exist but restore has never been tested |
| TLS | How certificates are issued and renewed | HTTPS is missing or renewal is unmanaged |
| Updates | Who patches the OS and server stack | No one owns the update process |
| SMTP | How Moodle sends email | Outbound mail is blocked or unconfigured |
| Monitoring | Whether CPU, RAM, disk, and network graphs are available | Problems are discovered only from student complaints |
Backups should be tested through restore
The reliability of a backup is proven by restoring it. A useful test is to deploy a separate Moodle copy, restore the database and files, then verify login, several courses, and access to uploaded content.
Manual schemes may use mysqldump, pg_dump, snapshots, and file-level backup tools. The choice depends on recovery requirements and acceptable downtime. A full VPS snapshot is convenient, but it should not automatically be treated as the only backup, especially when it remains inside the same infrastructure.
Why should the database and moodledata represent the same point in time?
There is a less obvious problem: the database backup and moodledata backup can both finish successfully while representing different moments in time. During a long file copy, users may continue submitting assignments, changing courses, and taking quizzes. The database may reflect one state while some files in the backup reflect a later state.
For a small learning platform, that risk may sometimes be accepted. For a critical Moodle deployment, decide in advance how consistency will be achieved: a maintenance window, coordinated snapshot, platform backup mechanism, or another method that produces a predictable restore point.
The final test is still recovery. After restore, inspect courses, recently uploaded files, quiz attempts, and several user actions. A backup that exists only as an archive in the control panel has not yet proven that recovery works.
Short rule: backup means a copy exists. Recovery means you have proved that the copy can produce a working Moodle site.
Test email before production launch
Moodle uses email for notifications, password recovery, and other events. After migration, administrators often test the home page, courses, and quizzes but forget SMTP until the first important message fails to arrive.
Before launch, send a test message and verify SMTP authentication, TLS, and delivery to an external mailbox. If the hosting provider restricts outbound SMTP connections or the local MTA, find that out before production use.
Which security questions belong to the hosting decision?
Full Moodle hardening is a separate topic. For hosting selection, focus on operational responsibility: who installs security updates for the OS and PHP, who manages the firewall, whether administrative access can be restricted, how moodledata is protected, and where snapshots and backups are stored.
On an unmanaged VPS, these tasks normally belong to the owner or system administrator. A managed service may take responsibility for part of them, but the boundary should be clear before launch. The phrase “managed server” does not tell you who patches PHP, who responds to a vulnerable package, or who restores the VM after failure.
Also ask who can access snapshots and whether a new VM can be restored from them quickly. That is not abstract security policy; it is part of the real recovery process.
How do you test Moodle hosting before migration and verify the plan?
The best final check is a copy of the real site, or a test environment that is close to production in Moodle version, plugins, database, and user workflow. Loading the home page once tells you almost nothing about whether the server will survive a mass quiz.
A practical acceptance test can be split into several stages.
- Check Environment. Verify PHP, extensions, PHP settings, and database compatibility.
- Check cron. Run CLI cron manually and verify Scheduled tasks.
- Record resource limits. Find out CPU, RAM, PHP process, I/O, and other hosting limits.
- Deploy a realistic copy. The closer the plugins, database, and
moodledataare to production, the more useful the test becomes. - Reproduce normal usage. Login, browse a course, open resources, submit an assignment.
- Reproduce the peak. For example, mass login followed by simultaneous quiz start.
- Test recovery. Create a backup and prove that the site can be restored from it.
During the test, observe several layers at once. CPU alone can hide a PHP-FPM queue. RAM alone can hide slow storage. Browser response time shows the symptom but not the source.
| Check | What to do | What to watch | Bad sign |
|---|---|---|---|
| Environment | Run Moodle Environment check | PHP, extensions, DB | Compatibility errors |
| Cron | Check Scheduled tasks | Runtime and queue | Tasks continuously fall behind |
| CPU | Run a typical workload | top, htop, load |
Sustained saturation |
| RAM | Check memory during the peak | free -h, vmstat |
Low available RAM and active swap |
| PHP-FPM | Generate parallel dynamic requests | Active processes, queue, max children | Repeatedly reaching the limit |
| Database | Run a realistic Moodle workflow | DB CPU, connections, latency | The database becomes the wait point |
| Storage | Exercise file and database operations | I/O latency, iostat |
Disk latency rises with Moodle response time |
| HTTP | Observe user requests | Response time and status codes | More 5xx errors and timeouts |
| Backup | Perform a test restore | Database and file integrity | The site cannot be restored normally |
Why ApacheBench alone is not a realistic Moodle test
Sending hundreds of requests to one public page is easy, but that traffic looks very different from real Moodle activity. A user authenticates, maintains a session, opens different pages, interacts with the database, submits data, and changes application state.
Tools such as JMeter and k6 can reproduce a sequence of requests and user sessions. But the workflow should be defined before choosing the load-testing tool.
For example:
- user logs in;
- opens a course;
- opens a quiz;
- starts an attempt;
- submits several answers;
- finishes the attempt.
If the exam is the most dangerous workload peak, test the exam workflow rather than a static home page.
Run load tests only against infrastructure you control or after getting permission from the hosting provider. Aggressive testing on shared hosting can affect other customers and violate service rules.
How do you know Moodle has outgrown the current hosting?
A migration is justified when the problem is repeatable and a specific limit can be demonstrated: CPU is repeatedly saturated, RAM is insufficient, PHP-FPM repeatedly hits its worker limit, the database becomes the bottleneck, storage latency rises, or shared hosting restricts processes and cron in ways that cannot be fixed on the current plan.
“Moodle is sometimes slow” is too vague. A better troubleshooting chain is:
- identify the exact user scenario;
- reproduce the symptom;
- collect a metric or log entry;
- identify the limiting component;
- change configuration or hosting resources;
- repeat the test.
Resource headroom should also be judged this way rather than by a universal percentage. If the expected normal peak already pushes one component to its ceiling, the configuration is too close to the limit. Healthy headroom means the expected peak can be handled without persistent queues, swap, resource faults, or major response-time degradation.
- Which PHP and database versions are available?
- Can Moodle CLI cron run every minute?
- What CPU, RAM, and PHP process limits actually apply?
- Is OPcache enabled and is Redis available?
- Is SSH and PHP CLI access available?
- What disk I/O and network traffic limits apply?
- Can RAM and vCPU be increased without a full migration?
- How do backup, retention, and restore work?
Checklist before making the final hosting choice
- The Moodle version has been selected and its server requirements checked.
- PHP version, extensions, and important PHP limits have been verified.
- The MySQL, MariaDB, or PostgreSQL version has been checked.
- Peak concurrency has been estimated, not just total accounts.
- Mass quizzes, exams, and assignment uploads have been considered separately.
- The location of video and large files is known.
- The current size and growth rate of
moodledataare known. - Free disk space and inode availability have been checked.
- Storage headroom exists for the database, temporary files, and backups.
- Cron can run at the required frequency.
- PHP CLI has been checked.
- PHP-FPM or PHP process limits are known.
- OPcache is enabled.
- There is a clear reason for using or not using Redis.
- CPU, RAM, swap, disk, and network can be monitored.
- The backup includes both the database and
moodledata. - Database and file consistency during backup has been considered.
- An off-server or otherwise independent backup exists.
- At least one test restore has been completed.
- Moodle email delivery through SMTP has been tested.
- A normal user workflow has been tested.
- An exam-style peak has been tested when relevant.
- The system retains headroom under the expected peak and can be scaled later.
Good Moodle hosting is not defined by one specification and there is no reliable “RAM per student” formula. Define the real workload peak first, then verify Moodle compatibility, cron, PHP-FPM, database behavior, and storage. After that, reproduce the user scenario that actually creates the load.
If the server handles that scenario without PHP queues, active swap, an overloaded database, significant disk latency, or 5xx errors, the configuration has a technical basis. If not, you now know which component limits Moodle and what needs to change. That is far more useful than simply buying the next plan with twice as much RAM.


