LifterLMS Logo

How Fast is LifterLMS? Pressidium Speed and Stress Test

Will Middleton from LifterLMS and Christos Paloukas from Pressidium do a stress test of LifterLMS with a high number of users to see what the concurrent user breaking point is.


How Fast Can LifterLMS Handle on a Managed WordPress Host

Will Middleton from LifterLMS and Christos from Pressidium ran a live load test on a plain LifterLMS install with every add-on active. The site held roughly 300 to 400 millisecond response times through 100 concurrent users and peaked at 1.9 seconds at 250 concurrent users while returning almost no errors. This walkthrough covers the setup, the results, and where real LifterLMS sites hit their limits.

What You’ll Learn

  • The difference between front-end performance testing and back-end (protocol-based) load testing
  • The four categories of load testing, load, stress, spike, and endurance
  • What actually happened when a LifterLMS site was hit with 250 concurrent users on Pressidium
  • How multi-tier hosting infrastructure uses caching and app-tier nodes to serve LMS traffic
  • The two spots in a real LifterLMS site that most often cause slowdowns, big quizzes and the Course Builder

Key Takeaways

  • Write your objective before you test. “Support 300 quiz submissions during finals with under a 2 second response time” is a real objective. “The site is slow” is not.
  • Test both back-end capacity and front-end rendering. Passing one and failing the other still gives users a slow site.
  • Host as close to your learners as you can. Latency between the visitor and the server is a real chunk of response time.
  • Cached anonymous pages are cheap. Logged-in traffic, quiz submissions, and course builder screens are where hosting resources actually matter.
  • Never trust a single test. Run it three or four times before drawing conclusions.

Frequently Asked Questions

What is the difference between front-end and back-end performance testing?

Front-end testing measures how fast a browser renders a page, using tools like Lighthouse, GTmetrix, and PageSpeed Insights. Back-end (protocol-based) testing simulates many concurrent users hitting the server and measures how the hosting infrastructure holds up under load. You need both to see the full picture: back-end tests confirm your host can serve traffic, and front-end tests confirm real users see a fast page.

Why are third-party resources like CDNs and Vimeo excluded from a back-end load test?

The point of a back-end test is to measure how your own hosting infrastructure responds. A third-party CDN or an embedded Vimeo video is served from someone else’s server, so timing it tells you nothing about your host. Including those requests also gets expensive fast because a load test fires thousands of requests per second.

What happens when a LifterLMS site hits the concurrent user limit of its hosting plan?

Response times climb first, then errors start returning. In the Pressidium test, a plain LifterLMS install with every add-on installed held roughly 300 to 400 millisecond response times up through 100 concurrent users, and pushed to 1.9 seconds at 250 concurrent users while still returning almost no errors. When a plan is genuinely overrun, you add more nodes at the hosting layer, especially at the app tier where logged-in requests are served.

How does hosting caching speed up a LifterLMS site?

A good WordPress host uses a two-layer cache: an in-memory layer that returns pages in milliseconds, and an SSD layer behind it for high availability. Anonymous pages like the home page, courses index, and blog posts serve from cache. Logged-in requests, quiz submissions, and registration have to bypass the cache and hit the app and database tiers, which is where LMS load actually lives.

Do large quizzes and complex course structures slow down a LifterLMS site?

Yes. Two spots come up over and over in real LifterLMS sites. Very large quizzes with 100 or more questions hit the database hard on submit and can take 5 to 20 seconds to load. The Course Builder screen also gets heavy when a course has dozens of sections and hundreds of lessons expanding into one interface. Both of those are places where dedicated hosting resources at the app tier make a real difference.

Where should a LifterLMS site be hosted for the best performance?

As close to the user base as you can put it. Response time includes network latency between the visitor’s ISP and your server, so a US-based audience served from a European datacenter will always feel slower than the same site served from the US. When the audience is global, use multiple regions or put a CDN like Cloudflare in front of the static assets.

Is one performance test enough to know how your site will hold up?

No. One test is indicative, not conclusive. Performance is noisy, so run the same test three or four times to see whether the pattern holds. Every test should also start with a written objective, for example, “the site has to handle 300 concurrent quiz submissions during finals week without exceeding a 2 second response time.” Without an objective you cannot judge whether a result is good or bad.

Full Webinar Transcript

Why Hosting is the Foundation of a Fast WordPress LMS

Will Middleton: We are live. Hello everyone, it is Will Middleton from LifterLMS, and today I am joined by Christos from Pressidium. You might recognize him from another webinar we did recently around decoding hosting in WordPress and helping myself and other LifterLMS users understand what hosting is and how it works. There is a link to that other stream in the description here.

Today we are diving into a topic that everyone is interested in, LifterLMS and Pressidium, and we are going to be looking at speed and how many concurrent users you can have on a LifterLMS website hosted on Pressidium. Christos is coming in from Athens, Greece today, and I am in Berlin, Germany.

We are diving into a couple of tests around benchmarking, capacity planning, and where you should put an application on what hardware. We talk a lot in WordPress about the software and the plugins and themes aspect of things, but the hosting is the fundamental layer. The hardware is the application itself. It is really important to understand how these things work, so I am super excited to have Christos joining me today to demonstrate some of the hardware aspects of running a successful WordPress website.

We are working with a fresh WordPress install with no users yet, using the LifterLMS core plugin along with all of the add-ons installed and a clean database. Thanks so much Christos for joining me, you want to take it away?

Christos: Yeah, thanks for that great intro. I will go straight to sharing my screen.

The Four Pillars of Web Performance

Christos: Performance. Everyone talks about performance in websites, and there are quite a few different types. Performance really revolves around four cardinal metrics.

Reliability. Does it always work? “Always” is in italics because nothing always works. Stability. Does it work well? Speed. Is it fast or is it slow? And scalability. What can we do to sustain performance and still be able to handle more parallel concurrent users, or more traffic on your website in general?

Will Middleton: Yeah, that makes sense to me. Performance is obviously something we use all the time when we talk about plugins, themes, and hosting, and I like how you break it down. There are four different main components to look at when testing for performance.

A Real World Example of Performance in Action

Christos: I can give a real world example. Picture yourself in the midst of the festive frenzy, maybe Christmas or another holiday, and you go to the grocery store. I am sure it has happened to all of us at some point. You get there and you cannot find enough parking space. That is scalability.

Then the number of customers in the store, the store’s layout, the way it is organized, all of these things add to the writhing of customers running around and causing havoc rather than actually getting their products and going to the checkout. A long checkout line is another example. And is your literal plugin up to date? Is it stable? That is going to affect performance too, because with every update there are usually performance improvements coming from your end, that is what I see in my testing.

Will Middleton: Yeah, there is a lot of emphasis on performance and how we optimize the way we store things and serve them from the database, to try to make it more performant on scalability and stability. That was a big project on your end too. I remember when you were trying to move away from the stereotypical meta-table approach.

Front-End Versus Back-End Performance Testing

Christos: Two types of performance testing. Everyone is more familiar with front-end performance testing, browser-based, the kind that uses Lighthouse. First contentful paint, LCP, speed index, the metrics that PageSpeed Insights and GTmetrix report. That is about how the browser interacts with the site, how things load and render, and it is a user experience lens.

We are going to talk more about protocol-based, server-side performance testing. You have a testing engine that simulates real world scenarios with multiple concurrent virtual users hitting your website. The main components are a test plan, which tells the engine what to do. It is essentially a user journey.

A simple user journey might be: someone goes to the home page, they log in, they go to the product catalog, they select a product, they add it to the cart, then they check out. You can take that user journey and insert it into the testing engine so it simulates every step. With LifterLMS, when you take a quiz there is a little progress recorder that fires a small request from the browser to the server to record your progress. You do not see it as a user, but that is one of the things we test with protocol-based, server-side testing.

Will Middleton: So an external server is basically pinging your website with a bunch of requests that mimic user behavior, in order to test speed and performance?

Christos: In a nutshell, yes. The attacking party, let us call it that, sends the traffic. Then all of the information has to be taken back and collected. That is time series data, always in relation to a certain point in time. The main metric we focus on is response time. We want to see how long it took to make a request for lifterlms.com, and how that changes over time and with increased stress. Increasing concurrent users just means more requests, but it is literally another user going through that same test plan. There are always think times, always random pauses, to make it feel closer to real traffic.

Setting Clear Objectives Before You Test

Christos: That brings us to the first objective. Before you run any test, you want to get your objective clear. An objective could be, I want to optimize the speed of my home page so PageSpeed Insights gives me a better score. You cannot just say, my site is slow.

For protocol-based testing, an objective might be, I am expecting a surge of traffic for Black Friday, and I want to make sure first that my website will not go down. That is reliability. And that it will still be able to support all these concurrent users. That is scalability and stability. And your users still get a decent user experience. It probably will not be the same as any other day, but you do not want them waiting 20 or 30 seconds, let alone getting a timeout during checkout.

Will Middleton: Right, they are just browsing products, you do not want them to bounce.

The Four Categories of Load Testing

Christos: I want to describe four categories of protocol-based testing. I like to think of load testing as the greater category. Some people will argue with me about the naming. There are far too many names for far too many different types of tests out there.

Load testing. Running a set number of concurrent users over time with a test plan, trying to simulate a normal expected day. If you are changing hosting providers and you want capacity planning, this is what you would ask for.

Stress testing. Same thing, but we try to break it. We see where the breaking points are and push it to its limits. Stress testing actually poses a problem for the infrastructure doing the test itself. One server cannot handle an infinite number of requests, so you need multiple testing servers working in a distributed setup.

Spike testing. Sudden, short bursts of traffic. Maybe you have a campaign going on, or a TV commercial. You might expect 500 to 1,000 concurrent users within five minutes hitting your site, and you want to see how your provider or your website holds up.

Endurance testing. You try to find long-term problems, like memory leaks or resource over-consumption over time. You might run one test for three or four hours.

Will Middleton: That makes a lot of sense. Load is what we can handle day to day, spike is if I promote my site on the news and expect a ton of users at once.

Building the LifterLMS Test Plan

Christos: Now the juicy part, we are getting into the actual test. I created two different tests. They both use the same test plan, but the type of test is different.

The first test is going to benchmark a LifterLMS website. It is a plain vanilla installation of LifterLMS 6.3, and as Will said, the latest version of every LifterLMS plugin, not just the core one.

The test plan runs this routine. The virtual user goes to the home page, waits a certain amount of time, goes to the registration page, submits the actual registration by clicking the register button, goes to the courses page, hits the login page, and so forth. The user logs in, goes to the courses page, starts a course, goes into the course, takes the course, and does the quiz at the end. They submit the quiz and the routine is over. Then that virtual user starts over, over and over again.

Will Middleton: So one server is basically sending a bunch of fake users running this routine to our website to test it. Each user is doing the same routine simultaneously.

Christos: Yes, and there are multiple servers doing it because of the distributed setup. It is a whole set of infrastructure with a couple of different layers.

Why Third-Party Resources Are Excluded

Christos: For each request there are also embedded content requests. When you visit lifterlms.com, the browser then requests other things, maybe the Google Fonts API, Google Analytics, Google Tag Manager, your site’s JavaScript, CSS, images. All of that gets requested after the initial page load. Protocol-based testing hits all of those, but it does not render the scripts and styles.

It is important to only include resources hosted on lifterlms.com itself. You do not want images pulled from a CDN in the test, because you are not testing the response time of the CDN, and it is going to cost you a lot of money in requests.

Will Middleton: So if we had a video hosted on Vimeo and it was embedded on our home page, we are not looking at the response time of Vimeo loading in the video, we are just looking at our own server’s response.

Christos: Right. I can show that on the fly. In Chrome DevTools I open the Network tab on pressidium.com. Every request in there that is a CDN, Google Tag Manager, or a third-party analytics call is filtered out of the test. Those requests are not going to help us, they are not on our server.

Will Middleton: Cool. And those other resources would be accounted for on a front-end test like GTmetrix.

Christos: Yes, but for good reason we exclude them here. We cannot optimize them. There is nothing LifterLMS can do to make Vimeo faster, that is on them. GTmetrix, PageSpeed Insights, and WebPageTest, anything that uses Lighthouse, all of that is still important. It just measures a different thing.

Reading the Grafana Test Dashboard

Christos: For the actual test, we are going to ramp from 0 to 250 concurrent users. It will take about seven minutes to ramp up, then hold for two minutes, then we will cut those users off at two users per second on the way down.

Right now we are looking at the results page. We use Grafana for this. The results feed a test engine layer, then a data collection layer below it, and then a database layer, which is what Grafana reads from.

The top of the dashboard shows the summary. Total requests. Maximum number of concurrent users running at any point, that is the purple line in the graph. Response times, the mean response time value for the entire test. Minimum response time. Success rate, error rate, all shown for the whole test. You can zoom into a segment inside Grafana to get the mean for just that window.

Throughput here does not mean network throughput. It is the throughput of how many requests per second are being pushed out of our testing infrastructure to the website.

Understanding Concurrent Users and Requests Per Second

Christos: As concurrent users increase, requests per second usually increase along with them. There might be little spikes and dips, that is normal when you are trying to simulate real users. For this benchmarking test though, we are just going to go straight up, hold at the top, and then gradually stop.

Will Middleton: So we are slowly increasing the amount of concurrent users on the website, and the requests are climbing with them?

Christos: Yes. There are threads of concurrent users running, and those threads are executing requests based on the test plan we walked through.

Will Middleton: And at some point we are expecting this to max out and crash?

Christos: The test is going to keep running the users, whether or not the site holds up. The infrastructure does not care. If the site goes down we get a bunch of errors, and there is an error panel that describes them. The test just runs the routine all the way through and whatever happens happens.

Response Time and What It Really Measures

Christos: The second graph shows response time layered over concurrent users and requests per second. Here we are looking at the parent request. If it is lifterlms.com, that includes all of the embedded content on our server. Everything has to get loaded before we record the response time, because as a user you do not really care if the CSS is going to take a little longer. You see the end product.

At a quick glance, 100 concurrent users are pushing out 800 requests per second, and the response time is 369 milliseconds. All those files come down in 369 milliseconds, then your browser renders them and you have a web page.

The next two little graphs are successful requests and failed requests. So far no failures, which is concerning, I like failures. That is room for improvement.

Making Sense of Percentiles

Christos: This next section is only response time. It shows what request is being made, how many of them there are, the mean, the median, the mode, and the percentiles. The 99th percentile had this response time in seconds, the 95th had this one, then the minimum, the maximum, and more math.

Will Middleton: So if we take a look at the 99th percentile, that is the slowest end of it. The 50th percentile is the user at the middle who gets a faster response, and as we increase up to 90 or 95 they start to get slower response times.

Christos: From the end user side, yes. From a technical standpoint, no two requests will ever be alike. It is always good to exclude outliers. If the top 1 percent has response times of five seconds but you are at 300 milliseconds, you probably do not care. Something happened network-side, between the person’s computer, their ISP, the internet, everything in between.

That is why region is important. You want your site hosted as close to your user base as possible. Multiple regions, or a CDN like Cloudflare for caching in multiple areas.

The First Benchmark, 250 Concurrent Users on LifterLMS

Christos: We just made it to the peak of the test, 250 concurrent users. Think of it like university exams. There is an eight-minute ramp up period, that is all those users logging in and trying to access the course or the quiz.

Will Middleton: So if you are hosting a university or a class with a specific deadline for a quiz, we might see a lot of users logging in at that certain time. Maxing out at about 250 concurrent users. That is 250 people on the website at the same time going through the full user flow.

Christos: Yes, and repeatedly, with the think times in between. Otherwise requests per second would be at the tens of thousands.

At the peak we are hitting 1,500 to 1,700 requests per second. LifterLMS on this environment has a 1.25 second response time at peak, and the results are aggregated into 10-second intervals. The max was around 1.9 seconds when it peaked, which is not bad. You have anywhere between 1,500 and 1,700 requests per second, and response time is pretty steady at 1.3 to 1.9 seconds. The breaking point is somewhere higher up than we pushed today.

The Two-Layer Adaptive Caching Model

Christos: The next panels drill into each request individually. Registration is an uncached request, which means it passes the caching layer. In our architecture we have a front-end array that does all the caching plus most of the security work. Anything that cannot be cached goes past that to the back-end app layer, which is where the web server actually runs your PHP. That app tier talks to the database layer when a request needs something fresh, like a POST or a logged-in page.

The caching layer itself has two separate parts. There is an in-memory layer, and there is an SSD layer behind it. Most sites fit entirely in memory. The SSD part is not used, but it is required for high availability inside a high-availability caching setup. I just made that sound cool.

Will Middleton: So the caching layer stores certain things in dedicated resources so they can be served faster. But there is other information that cannot be cached, like user registration, because we need to talk to the database.

Christos: Right. There is an asterisk. Some content you can cache in very specific ways if you know your website well enough, even when a user is logged in. But typically, logged-in content is not served from the cache.

Response Codes and Browser Caching

Christos: The dashboard also has response code panels. A 304 is the browser cache saying, hey, that page has not changed on the server, so I will use the copy I already have. The browser pops it up faster because it did not have to download it again.

In this test routine, browser cache is cleared per thread and per iteration. Every time a virtual user starts the routine over, their cache is cleared and they start from scratch. That keeps the numbers honest, because otherwise later iterations look faster than they really are.

Where Errors Come From, and How to Read Them

Christos: In this test we got one of each error type. That is fine. The error panel gives you the details for whatever error came back. If it is HTTP-oriented you get the full HTTP request and response, plus the headers. Not seeing any errors across millions of requests is not honest. If an online testing tool tells you it ran a million requests with zero errors, that is a red flag, not a badge of honor.

The Network Tab, Latency, and Connect Time

Christos: The Network tab has four charts. Latency over time is the same as time to first byte, the request goes out, and we measure the time to the first part of the response coming back. Connect time is the time between pushing enter on your keyboard and the connection being established, that is the TCP three-way handshake for the folks who care. Processing time is the time the server spends actually doing something before the response comes back. Latency equals connect time plus processing time. Throughput over time is the amount of bytes going out and coming in from the client’s perspective.

Second Test, Capacity Planning with Stepped Load

Christos: For a real client we would run a longer test, and we would use steps. Rather than one long ramp, we ramp up, hold for 60 seconds, ramp again, hold again. For a real client the holds would be 240 seconds. This is more useful for evaluating the system side of things. Every time we bump concurrent users, we expect a brief spike in response time, and how the cluster balances out from there tells us how good the infrastructure is.

I went up to 300 users, held 60 seconds at the peak. Hold time is added between each step, and ramp down is gradual too. If you just cut users off at the end, you get timeouts and huge response times, because there are still requests in flight that never got a reply. I do not filter those errors out. I do not filter any errors out.

Will Middleton: Once you identify errors, or you have a client that needs a faster load time or more concurrent user capacity, do you dedicate more nodes to the plan?

Christos: Yes. We also have the capability as a host to dig deep into the actual request and single out a particular set of requests, whether it is login or a specific course, and attribute resources at the app tier to those requests.

Why Server-Side Resource Metrics Matter

Christos: What is missing from a lot of load testing tools is the actual system resource picture. What about CPU? What about memory? What is happening from the server’s perspective? If you go to a third party that does load testing, they cannot show you that. Even if you brought us a site that is not hosted with us, we could not show it either. It is important as a provider to have this bird’s-eye view.

The first check is that the request-per-second number the testing infrastructure reports matches what the server reports. If they diverge, something is wrong with the test. In our case they line up.

Reading Load Averages and Memory Usage

Christos: Beyond requests per second, we look at Apache connections, PHP-FPM workers, load average across the cluster, and MySQL statistics like the number of database connections and the volume of MySQL traffic.

The n-tier architecture shows up clearly here. The load balancer nodes (LB1 and LB2), the blue and yellow lines, that is the front end where all the caching happens. You can see there was almost no significant load on the caching side, because caching for a site this simple happens in memory very quickly. Three as a load average at 1,800 requests per second is not bad at all. It means the machine is barely working.

The app servers show more activity, because that is where the back-end work happens. Everything the logged-in user is doing, the quiz submissions, the progress recorder, the courses page, all lives there. That is usually where we add more nodes for an LMS site, because most of the heavy lifting is for logged-in users.

Memory usage tells the mirror story. The caching layer serves out of memory, so memory usage on the load balancer nodes is high and CPU stays low. The app tier uses less memory but more CPU.

Memory Versus Disk Performance

Will Middleton: When you talk about SSD versus memory, we are talking about how the server stores information. Memory is faster than reading from disk, right?

Christos: Yes. Memory is always faster than loading something from a disk. When I got my first SSD I took videos of my computer restarting and sent them to friends, because SSDs work more like memory than like spinning disks. A spinning disk has to wait, even on lightweight Linux, but an SSD just goes.

The caching engine holds hot pages in memory. It will not evict them because it thinks they are irrelevant, because the whole point of the layer is that they stay hot.

Investigating the Errors on the Second Test

Christos: Test is over, we picked up 90 errors this time. That is interesting, so let me pull them up. The difference here is that we used stepped ramps. There is generally a small spike in response time each time we add users, then the cluster balances it out.

Looking at the graph, the spike lines up with the tail end of the test when we ramp users off. As I mentioned, those errors are usually not the site’s fault, they are requests that were mid-flight when the test cut the user off. Everything is uniform, and the nature of the errors does not concern me.

Reading the Results Like a Hosting Provider

Christos: If I were writing this up for a real client, and this was a longer proper test run three or four times, I would say something like: at a steady 100 to 150 concurrent users on this plan and this site, you sit around 300 to 400 milliseconds. During peaks up to about 280 to 300 concurrent users, you sit around 2 seconds. That is 4 to 5 times the base response time, but if the customer is comfortable with a 2 second response time during peak, they do not need more hardware, and we do not need to charge them more.

This is half the picture though. I still have to see what happens on the browser side. If the site is poorly designed on the front end, these back-end results do not save it. You have to see both sides.

Where LifterLMS Users Actually Hit Limits

Will Middleton: A common thing I hear about from users is very large quizzes. A quiz with 100 or 200 questions can take 5, 10, or even 20 seconds to load. That is a common point where LifterLMS users run into issues with a web host, because the quizzes have so much data in them and so much database interaction.

The other spot is the Course Builder. When you use the Course Builder in LifterLMS, you have sections and lessons inside one interface, and you can expand them all. If you have 50 sections with 5 lessons each, now you have 250 lessons trying to populate on the page at the same time. The quiz load and the Course Builder are the two spots where I see users at scale hit issues.

Christos: That is worth testing. If the request has a very large payload and there are many users making that request all the time, that is exactly where you would attribute more resources at the app tier. I would actually attribute resources to the whole course rather than just the quiz, so a node is not sitting idle waiting for someone to hit the quiz. It shares load with the rest of the course traffic too.

Why Both Front-End and Back-End Testing Matter

Will Middleton: That is a super important point. When a web host talks a lot about optimization, or when you find a cheaper web host and they talk about “speed optimization,” they are usually talking about the back-end side of things, deliverability from the server. But some of that caching or advanced CDN work can actually cause issues on the front end for real users. It is important to consider both sides.

Closing

Will Middleton: That was a great demonstration. Thank you so much Christos for coming on and showing the back-end load testing you do at Pressidium to figure out how to optimize web host delivery. If you want to learn more about Christos and the work Pressidium is doing in the WordPress hosting industry, go to pressidium.com, or find the link in the description to this video, or on our recommended resources page.

Leave a comment on this video wherever you see it and tell us what content you want next. We will keep making content that demystifies what web hosting is, how it works, and what to consider when buying it. Thanks everyone for joining us live and on the replay.

Christos: Thank you.