Moodle Concurrent Users: Your Server's Real Capacity
Enrolled doesn't mean concurrent. Discover the calculations, metrics, and load testing methods to reveal your Moodle's true capacity before online exams.
by Cleverson Gouvêa

How many concurrent Moodle users your server can handle is the question every online learning administrator asks a week before an exam, and almost never with the right number in hand. The answer isn't in the server's specifications or the hosting plan. It comes from a load test, three or four metrics you can read today, and a simple calculation I'll show you step-by-step.
TL;DR
- "Concurrent" is not "logged in." What crashes Moodle is requests being processed, not the list of online users.
- Moodle's own documentation estimates that, in the worst-case scenario, a site only serves 10 to 20 concurrent users per GB of RAM. Use this as a baseline, not a target.
- The real bottleneck is almost always one of these four: PHP-FPM workers, sessions, cache (MUC), or the database.
- The worst documented scenario is a large class clicking "Start attempt" on a timed quiz, all in the same minute.
- To safely estimate Moodle concurrent users, combine a memory calculation with a load test that simulates your most demanding exam.
- Load testing should be done in a staging environment, using Moodle's own JMeter plan generator. Never in production.
What "Concurrent Users" Really Means
Before any calculations, align the terminology with your team. In practice, an institution often uses three different numbers as if they were the same:
- Enrolled Users: how many accounts exist. A university with 12,000 enrolled students might have 300 people on the platform on a Tuesday afternoon.
- Online Users: Moodle's "Online Users" block shows who has been active in the last few minutes (by default, 5). This is a time window, not actual load.
- Concurrent Users: Moodle's performance documentation defines these as "those users for whom the server is actively doing something." This is the number that matters.
The difference is significant. A student reading a PDF for 15 minutes appears online but doesn't burden the server. However, 400 students who click "Submit" within the same 30-second window will consume all resources.
Why Synchronized Exams Are the Worst Case
Moodle itself explicitly states this: the worst possible scenario is a large class starting a timed quiz at the exact same time. Each "Start attempt" creates database records, selects questions, saves the session, and loads the first page. Multiply that by 800 students in the same minute, and you have a peak that won't show up in any monthly average.
When I size Moodle concurrent user capacity for an institution, the first question isn't "how many students do you have." It's "what is the largest synchronized exam of the semester, and how many students take it at the same time?"
The Official Rule of Thumb and Why It's Misleading
Moodle's Performance FAQ provides the most cited estimate in forums: "very roughly, in the worst case, your Moodle site may only serve 10 to 20 concurrent users per GB of memory." The same text notes that Moodle can easily use more than 50 MB of RAM per process, sometimes much more.
By this rule, a 16 GB server would handle between 160 and 320 concurrent users. I've seen a well-tuned 16 GB server handle much more than that during regular browsing, and I've seen a 32 GB server crash with 300 students during an exam. The rule serves only one purpose: to detect when the server is clearly undersized. For planning true Moodle concurrent users, it's insufficient.
Moodle's installation page is even more candid. It states that a forum post like "what hardware do I need for 50,000 users?" will rarely get a useful answer. The number depends on configuration, operating system, and, most importantly, what students are doing.
What Changes the Calculation
| Factor | Effect on Capacity | Where to Look |
|---|---|---|
| Activity Type | Quizzes and assignment submissions weigh much more than page views | Logs by time and by module |
| File sessions vs Redis | Disk-based sessions lock requests from the same user in a queue | config.php and Site administration → Plugins → Caching |
| OPcache | Without it, each request recompiles PHP | Moodle environment report |
| Untuned Database | Slow queries keep PHP-FPM workers busy | MariaDB slow query log |
| Third-party plugins | A poorly written block on the course page multiplies queries | Performance info in the footer |
| Delayed Cron | Accumulated tasks compete for CPU during class hours | Scheduled tasks |
The Calculation I Do Before Any Test
A load test confirms a hypothesis. Before that, you need the hypothesis. It comes from three measurements that any Linux server provides.
1. How Much Each PHP Process Consumes
With the environment in real use, measure the average memory of php-fpm processes (ps or your monitoring dashboard will show this). In Moodle, values between 60 and 120 MB per process are common; heavy plugins and reports drive it higher.
2. How Much RAM Is Left for PHP
From the server's total, subtract the operating system, database (if on the same machine), Redis, and a safety margin. What remains is the PHP-FPM budget.
3. How Long a Request Lasts
Moodle's performance report and performance information show page generation times. A typical, well-tuned course page is in the hundreds of milliseconds range.
Hypothetical Example, With Round Numbers
A 16 GB server with the database on the same machine: reserve 6 GB for MariaDB, Redis, and the system. That leaves 10 GB for PHP. With 80 MB processes, this yields about 125 workers (pm.max_children). If each request lasts 250 ms, each worker handles 4 per second, and the server processes something close to 500 requests per second at its theoretical ceiling.
Now, translate that into users. A browsing student generates a page every 20 or 30 seconds, and each page triggers several AJAX calls. During an exam, behavior changes: everyone clicks at the same time. That's why the same server that handles thousands of students browsing can struggle with a few hundred starting a quiz in the same minute.
This Moodle concurrent user estimate is a starting point. The load test finalizes the number.
PHP-FPM: The First Limit You'll Hit
The PHP manual defines pm.max_children as the maximum number of child processes and states that it "defines the limit of concurrent requests that will be served." In the Moodle concurrent user calculation, this is the governing number. When all workers are busy, the next request enters a queue. The student sees the page "loading" and, if the queue grows, receives a 502 or 504 error.
The two most common errors I encounter:
pm.max_childrentoo low: the server has excess RAM and still queues requests. Typical of shared hosting or default distribution configurations.pm.max_childrentoo high: someone increased it to "handle the exam," but RAM ran out, the system started swapping, and the entire server became slower than before. More workers than memory is worse than a queue.
What to Monitor in PHP-FPM
Activate the status page (pm.status_path) and monitor three fields during peak hours: active processes, listen queue (requests waiting for a worker), and max children reached (how many times the limit was reached). If max children reached climbs every Monday morning, you already know where your ceiling is.
Also enable request_slowlog_timeout. It records a backtrace for every request that exceeds the defined time. In almost every diagnosis, the slowlog points to a specific plugin or query, not a "lack of server resources." If your environment is already slow outside of exam periods, start with the slow Moodle diagnosis guide before thinking about capacity.
Sessions and Cache: Where Moodle Stalls Without Using CPU
This is the most deceptive bottleneck because the server appears idle, yet students are still waiting.
Sessions
Moodle's session documentation explains that the file driver is the default for new installations, and that the database driver has "relatively low performance" and is not recommended for large sites. For scalability, Moodle points to in-memory drivers (Memcached or Redis) and read-only sessions for pages that don't require a write lock.
The session problem is the lock: while one student request holds the session, others from the same student wait. In a quiz with auto-saving and multiple AJAX calls, this becomes an invisible queue, reducing Moodle concurrent user capacity even with spare CPU.
Cache (MUC)
The Moodle Universal Cache stores application and session data. By default, much of it goes to disk (moodledata). The performance recommendations page cites a direct account: the biggest improvement a site made was installing Redis. On a server with slow disk or network moodledata, this change is the difference between handling and not handling the peak load.
Configuring in-memory cache and sessions is one of the first things I address when a client asks how many Moodle concurrent users they can serve, because it's inexpensive and the gain is significant.
Database: The Bottleneck That Appears Last and Costs the Most
When PHP-FPM and cache are well-optimized, the limit shifts to the database. This is the most expensive to resolve, so it's worth knowing how to recognize it.
The number one recommendation in Moodle's documentation for MySQL and MariaDB is to understand and adjust the InnoDB buffer pool. On a dedicated database server, it can occupy up to 80% of memory. If the database shares a machine with PHP, this calculation needs to be done alongside the worker budget, otherwise one will consume the other's memory.
Regarding connections, the same page states that it's usually not necessary to exceed 200 max_connections, even on heavily loaded servers. If you need more, the problem is likely slow queries holding open connections, not a lack of connections.
Read Replica
Since Moodle 3.9, it's been possible to point read queries to replicas. The documentation estimates this can offload 80% to 90% of the primary database's load. It makes sense for large installations with heavy reports or many concurrent accesses. For a school with 2,000 students, it's complexity without return.
The Moodle 5.x server requirements cover supported PHP and database versions. The point here is different: even the right database, if poorly tuned, can still become the ceiling for your Moodle concurrent user peak on exam day.
How to Perform a Moodle Concurrent User Load Test
Moodle has an official tool for this, and few people use it. It consists of two components of the tool_generator, both for development use only.
Step 1: Set Up an Identical Staging Environment
Use the same hardware (or proportional), same Moodle version, same plugins, same theme. A Moodle concurrent user test in a different environment measures something else. Moodle's JMeter documentation is explicit: do not run these scripts or JMeter against production, because they generate a lot of artificial data and push the server to its limits and beyond.
Step 2: Generate a Test Course
The test course generator creates courses of fixed sizes. Size M has 1,000 users and 100 assignments; L, 10,000 users; XL, 50,000. The command is php admin/tool/generator/cli/maketestcourse.php (in Moodle 5.1 and later, the code is located within public/).
Step 3: Generate the JMeter Plan
The test plan generator creates the .jmx file and the user CSV. In Moodle's current code, the sizes are:
| Size | Virtual Users | Iterations | Ramp-up (seconds) |
|---|---|---|---|
| XS | 1 | 5 | 1 |
| S | 30 | 5 | 6 |
| M | 100 | 5 | 40 |
| L | 1,000 | 6 | 100 |
| XL | 5,000 | 6 | 500 |
| XXL | 10,000 | 7 | 800 |
Before generating, define $CFG->tool_generator_users_password in config.php and activate developer debugging mode in the test environment. Run the plan in Apache JMeter, currently version 5.6.3.
Step 4: Simulate Your Exam, Not the Average
The official plan simulates browsing the generated course. To determine how many Moodle concurrent users your system can handle during the exam, also record your own scenario in JMeter: login, open the quiz, start attempt, answer, finalize. And reduce the ramp-up time: if the exam opens at 7 PM and everyone logs in by 7:02 PM, the ramp-up is 120 seconds, not 800.
Step 5: Read the Right Result
During the test, monitor: 95th percentile response time, error rate, PHP-FPM listen queue, CPU, RAM, and swap usage, and slow database queries. The number you're looking for is the point where the p95 spikes or the error rate rises above zero. That's your Moodle concurrent user ceiling for that scenario. Plan to operate comfortably below it.
When NOT to Perform a Load Test, and Other Pitfalls
Load testing is a tool, not a ritual. There are times when it hinders more than helps.
- Do not test in production. Not even "just for a quick moment overnight." The generator creates fake users and courses that pollute reports, and the peak load can disrupt cron jobs and nightly backups.
- Do not test before adjusting the basics. If OPcache is disabled or the session is database-backed, the test only confirms the obvious. Fix it first, then test.
- Do not test with a cold cache and draw conclusions from the worst number. Run a warm-up pass.
- Do not confuse the load generator with the server. If JMeter runs on an underpowered machine, it becomes the bottleneck. Use a separate machine.
- Do not test during upgrade week. Moodle 5.3, the new LTS version, was released on October 5, 2026, according to the official release calendar. Upgrading and measuring capacity at the same time mixes two variables. Upgrade in a staging environment, stabilize it, then test.
Costs That Catch Administrators by Surprise
The cost of the test itself is low: a technician's hours and a staging environment for a few days. The expensive cost is what comes next. Scaling Moodle concurrent user capacity by instinct (doubling the server "just in case") often doubles the monthly bill without resolving a session lock. And scaling without a planned migration can require changing servers mid-semester, which is exactly what a good guide to migrating Moodle without data loss tries to avoid.
Signs Your Server Is Already at Its Limit
You don't need to wait for the exam to crash to know. These signs appear beforehand:
- Response time spikes every Monday morning or during evening class hours, then returns to normal.
- 502 and 504 errors appear in short bursts in web server logs.
- PHP-FPM's
max children reachedincreases week by week. - Swap starts being used during peak hours.
- Students report that "the exam crashed on submission," but the team cannot reproduce it outside of peak times.
If two or more of these appear, your Moodle concurrent user capacity is already at its limit, even if no one has formally complained.
How Agathas Web Solves This
At Agathas Web, I've supported Moodle for over 15 years, and Moodle concurrent user peak capacity is the conversation I have most often with eLearning administrators. Here's what Agathas' managed Moodle hosting delivers for this problem, in practice:
- Moodle-optimized Stack: PHP-FPM with a dedicated pool per instance, tuned OPcache, sessions and cache (MUC) in Redis, MariaDB with weekly index optimization, and isolated cron on a dedicated worker.
- Read-only replica for heavy reports, so administrative queries don't contend with exam traffic for database resources.
- 24/7 Monitoring with Grafana, Prometheus, Sentry, and external UptimeRobot, with alerts to the technical team's WhatsApp. The monthly report includes resource usage and user peak.
- On-call Support During Critical Periods: entrance exams, exam week, and course launches have an on-demand scaled team.
- Staging environment in Professional and Enterprise plans, where you can test updates, plugins, and load without touching production.
- Pre-limit Notification: when usage approaches the plan's ceiling, we notify you in advance, generally 60 days before any operational impact, and plan changes are free of charge.
Plans range from Starter (up to 500 users) to Professional (up to 5,000) and Enterprise (over 50,000, with load balancer and dedicated infrastructure). In one such case, a public university experienced a peak of 4,500 sessions during entrance exams with no downtime; in another, a school network went from 4.2s to 1.1s TTFB by switching from generic hosting to a dedicated stack.
To get started, the path begins with a free diagnostic: we analyze your current Moodle setup and provide identified bottlenecks and a proposal within 3 business days. If you're coming from another provider, assisted migration takes up to 5 business days, with staging environment validation before go-live. You can request this via the Moodle hosting plans page or directly through WhatsApp.
Conclusion: Measure Before the Exam, Not During It
How many Moodle concurrent users your server can handle isn't a number found in the specifications. It's a number that's measured: an estimate based on memory and request time, tuning of PHP-FPM, sessions, cache, and database, and a load test in a staging environment that reproduces your most demanding exam.
If the next synchronized assessment is on the calendar and you don't know your Moodle environment's concurrent user limit, start today with PHP-FPM status and the slowlog. And if you prefer someone to measure, tune, and be responsible for peak performance with an SLA in contract, request a free diagnostic from Agathas Web's Moodle hosting.
Related posts

Upgrading Moodle 4.5 to 5: A Real-World Checklist
Moodle 5.3 LTS is out, and 4.5 loses security in October 2027. Database, /public folder, plugins, and rollback: practical roadblocks to your upgrade.

Achieving Moodle Accessibility: WCAG 2.2 Without Course Redesign
Your Moodle platform is WCAG 2.2 AA accredited. The real accessibility challenge is in your course content, which can be addressed by prioritizing fixes in 90-day cycles.

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.