JMeter vs Locust is a choice between a GUI and Python. Apache JMeter builds tests in a desktop GUI, saves them as XML (.jmx) files, runs each virtual user on its own thread and supports many protocols out of the box. Locust tests are Python classes in a locustfile; Locust runs thousands of users as lightweight greenlets inside one process, and that process uses one CPU core.
Both are free and open source: JMeter under Apache 2.0, Locust under MIT. To compare them on more than feature lists, we ran the same tests in JMeter 5.6.3 and Locust 2.47.0 against one local API. With 2,000 concurrent users, JMeter ran 2,039 threads and Locust 12. On an endpoint that answers instantly, JMeter reached 52,591 requests a second and one Locust process 2,720. And with one request in ten failing, Locust exited with code 1 while JMeter exited with 0.
Key Takeaways
- Pick by who writes the tests. Testers who prefer a GUI and no code are better served by JMeter; Python developers who want tests in code review are better served by Locust.
- Locust needs far fewer threads and less memory per user: 12 threads and about 250 MB for 2,000 users, against JMeter’s 2,039 threads and 1.2 GB.
- A Locust process is limited to one CPU core. Its default HttpUser topped out at 2,720 requests a second in our run; FastHttpUser reached 10,990, and four processes 25,600.
- Locust fails a CI run on any failed request by default. JMeter exits with code 0 however many requests fail, so a pipeline needs its own check.
- JMeter covers more protocols out of the box. Distributed load is free in both. LoadFocus runs JMeter scripts in the cloud, not locustfiles.
JMeter vs Locust at a glance
| Apache JMeter 5.6.3 | Locust 2.47.0 | |
|---|---|---|
| Licence | Apache 2.0, free | MIT, free |
| How tests are built | GUI, saved as .jmx XML | Python code (a locustfile) |
| Runtime | One operating system thread per virtual user | gevent greenlets; one process uses one CPU core |
| Needs | Java 8 or later | Python 3.11 or later |
| Built-in protocols | HTTP, JDBC, FTP, LDAP, JMS, SMTP, POP3/IMAP, TCP, Java, OS process | HTTP; contrib users for MQTT, MongoDB, PostgreSQL, Socket.IO, DNS; anything else via your own Python client |
| Load model | Thread Group (closed), Open Model Thread Group (experimental) | Users and spawn rate (closed), custom shapes in Python |
| Pass or fail in CI | Not built in: exit code 0 with errors | Exit code 1 on any failure, or your own rule in Python |
| Live view | Listeners in the GUI; console summary in non-GUI mode | Web UI on port 8089 |
| Report | HTML dashboard on request (-e -o) | HTML report (--html), CSV (--csv), JSON (--json) |
| Distributed load | Free client-server mode | Free master and workers; --processes on one machine |
Versions and limits are from each project’s documentation and the installed packages as of November 2026.
What is the main difference between JMeter and Locust?
JMeter is an application: you build a test plan in a tree of thread groups, samplers, extractors and assertions, and JMeter saves it as XML. Locust is a Python library: a test is a class whose methods are the user’s tasks, and anything Python can do, the test can do. The rest of the comparison follows from that, from code review and debugging to who on the team can own the tests.
A .jmx file is easy to read in the GUI and hard to read in a pull request, because it is long XML. A locustfile is short and reviewable, and it needs someone who writes Python. JMeter allows code where you need it through JSR223 elements in Groovy, and it ships a recorder that turns a browser session into a first test plan. Locust ships no recorder, so its requests start as code you write.
How do the scripts compare?
A Locust script is a short Python class that reads well in a pull request. A JMeter script is a tree of elements built in the GUI and saved as a long XML file, which is quick to build and hard to review line by line.
Our test logs in once per virtual user, keeps the token, then loops a product list request and an order request that sends the token. In Locust that is the whole file:
from locust import HttpUser, task, constant
class Shopper(HttpUser):
host = "http://127.0.0.1:8099"
wait_time = constant(0)
def on_start(self):
r = self.client.post("/api/login", json={})
self.token = r.json()["token"]
@task
def browse_and_order(self):
self.client.get("/api/items")
self.client.post("/api/orders", json={"sku": "A1"},
headers={"Authorization": f"Bearer {self.token}"})
In JMeter the same test is a Thread Group holding a Once Only Controller with the login request and a JSON Extractor, an HTTP Header Manager that sends Bearer ${token}, and the two requests. Built in the GUI it takes a few minutes; saved, it is a long XML file that few people review line by line. Our JMeter correlation guide covers the extractor step by step, and our Locust tutorial walks through a full locustfile.
Do JMeter and Locust report the same results?
Close, but not identical, and the difference comes from the load generator. Both measure from sending a request to receiving the response, so the measuring tool’s own work per request shows up in the numbers. That matters most when users never pause, because each user sends its next request the moment the last one returns.
We ran 20 users for 60 seconds, ramping up over 10, with no wait time between requests, against endpoints that take 20 ms. Locust ran twice, once with its default HttpUser (built on the Python requests library) and once with FastHttpUser (built on geventhttpclient):
| JMeter 5.6.3 | Locust HttpUser | Locust FastHttpUser | |
|---|---|---|---|
| Requests | 51,087 | 42,637 | 47,393 |
| Requests per second | 852 | 721 | 802 |
| Median | 21 ms | 24 ms | 22 ms |
| 95th percentile | 23 ms | 31 ms | 26 ms |
| 99th percentile | 25 ms | 37 ms | 35 ms |
| Errors | 0 | 0 | 0 |
The server answered in 20 ms every time, so the extra milliseconds are spent in the load generator. With HttpUser, Locust added about 3 ms per request at the median and more in the tail, and because the users never paused, that also cut throughput by 15%. FastHttpUser closed most of the gap at the median and 95th percentile, though not at the 99th. With realistic think time between requests, users spend most of their time waiting and the gap matters much less.
One reporting detail to know: Locust rounds response times before it stores them, keeping exact milliseconds below 100 ms, rounding to the nearest 10 ms up to 1 second and to the nearest 100 ms up to 10 seconds. Its averages use the exact values. JMeter stores every sample as measured.
Which uses fewer resources, JMeter or Locust?
Locust, in threads and memory, by a wide margin. JMeter runs each virtual user on its own operating system thread, and its launcher reserves a fixed 1 GB heap. Locust runs each user as a greenlet, a lightweight coroutine, so 2,000 users share a handful of threads. The catch is CPU: one Locust process runs Python on a single core, while JMeter spreads its threads across every core.
We ran 2,000 users against an endpoint that waits 1 second before answering, ramping up over 20 seconds, and sampled each tool’s process from start to finish. The laptop was also running other work, so we ran the pair twice; both pairs agreed:
| 2,000 users, 1-second responses | JMeter 5.6.3 | Locust HttpUser |
|---|---|---|
| Requests in 60 s (run 1, run 2) | 101,851, 101,144 | 93,996, 95,322 |
| Mean response time | 1,001.8 ms, 1,001.2 ms | 1,028.7 ms, 1,019.6 ms |
| Errors | 0 | 0 |
| Peak threads in the process | 2,039, 2,038 | 12, 12 |
| Peak memory (RSS) | 1,218 MB, 1,276 MB | 246 MB, 272 MB |
| CPU time per 1,000 requests | 0.24 s, 0.20 s | 0.41 s, 0.37 s |
Locust held 2,000 waiting users on 12 threads and a fifth of the memory. JMeter used less CPU per request, and CPU is the limit that bites first in Locust: its process sat at about 60% of one core here, which leaves little headroom. The memory gap is partly configuration, since JMeter’s 1 GB heap is reserved at start whatever the test needs.
How much load can one machine generate?
JMeter, when responses are fast: about 19 times a single Locust process with its default HttpUser, and about twice four Locust processes with FastHttpUser. To find each tool’s ceiling, we ran 50 users for 30 seconds against an endpoint that answers instantly, so the load generator is the bottleneck:
| 50 users, instant responses | Requests per second | CPU used |
|---|---|---|
| JMeter 5.6.3 | 52,591 | about 2.7 cores |
| Locust, HttpUser, 1 process | 2,720 | about 0.9 cores (its limit) |
| Locust, FastHttpUser, 1 process | 10,990 | about 0.9 cores (its limit) |
Locust, FastHttpUser, --processes 4 | 25,600 | about 2.3 cores |
The test server ran on the same 10-core laptop, so these are ceilings for this machine, not benchmarks. The pattern is what carries over: a single Locust process stops at one core, so to generate more load you add processes. When a process passes 90% of a core, Locust logs “CPU usage above 90%!” with a warning that this “may even give inconsistent response time measurements”; our single-process runs sat right at that line, at 0.87 and 0.91 cores. --processes forks workers on one machine (it is marked experimental and is not available on Windows), and --master with --worker spreads them across machines. FastHttpUser gave 4 times the requests of HttpUser on the same core; Locust’s documentation calls it a drop-in replacement in most cases, with subtle differences when you work with the client’s internals, such as cookies.
How do JMeter and Locust fit in a CI/CD pipeline?
Locust fits with less work, because its default is the opposite of JMeter’s. By default Locust exits with code 1 if any request failed, so one timeout in a long run fails the build. JMeter in non-GUI mode exits with code 0 when the test completes, however many requests failed, so a pipeline must check the results file itself.
We sent both tools to an endpoint that fails one request in ten. JMeter sent 200 requests from 10 threads, recorded 20 errors (10.00%) and exited with code 0. Locust ran 10 users for 5 seconds, sent 17,282 requests with 1,730 failures (10.01%) and exited with code 1.
Neither default is what most pipelines want. In Locust, a quitting event handler sets the exit code from your own rule, such as fewer than 1% failures and a 95th percentile under 200 ms:
from locust import events
@events.quitting.add_listener
def gate(environment, **kwargs):
total = environment.stats.total
if total.fail_ratio > 0.01 or total.get_response_time_percentile(0.95) > 200:
environment.process_exit_code = 1
else:
environment.process_exit_code = 0
With this handler the failing run still exited with 1, and the same test against a healthy endpoint exited with 0. For JMeter, parse the results file in a script, use a CI server’s performance plugin, or run on a platform that applies thresholds. For the report side, JMeter builds its HTML dashboard report when asked with -e -o, and Locust writes an HTML report with --html.
How do JMeter and Locust model load?
Both default to a closed model: a set number of users, each sending its next request after the last one returns. In Locust you set users and a spawn rate, and each user’s wait_time decides its pause: between for random think time, constant_pacing or constant_throughput to fix how often each user runs its tasks. A LoadTestShape class changes users and spawn rate over time in Python.
JMeter sets users and ramp-up in a Thread Group and adds pauses with timers. Since 5.5 it also has the Open Model Thread Group, still labelled experimental, which schedules arrivals at a rate however slowly the server answers. Locust has no arrival-rate mode built in, so when a server slows down, a Locust test sends less load. Our guide to JMeter thread groups shows how differently the two models report the same overloaded server.
How do you scale JMeter and Locust beyond one machine?
Both do it for free. JMeter’s client-server mode sends the test plan from one controller to several JMeter servers, which generate the load and stream results back. Locust’s master hands users to worker processes, on one machine with --processes or across machines with --worker, and combines their statistics in one web UI. In both cases you set up and run the machines yourself.
Locust needs more workers for the same load because each is limited to one core, so plan one worker per core. JMeter needs fewer machines for HTTP but each one carries a large heap and thousands of threads at high user counts. Our JMeter distributed testing guide covers the JMeter setup step by step.
When to choose JMeter and when to choose Locust
Choose by your team and your protocols first, and by resource use second. Both produce trustworthy numbers once the load generator has headroom. JMeter suits testers who work in a GUI and plans that mix HTTP with other protocols; Locust suits Python developers who want tests in code review and a CI exit code with no extra tooling.
Choose JMeter when:
- testers who do not write code own performance testing;
- you test databases, mail, FTP, LDAP or JMS alongside HTTP;
- you need high request rates from few machines;
- you want to record a browser session as a starting point, or run your tests on a cloud service that accepts .jmx files.
Choose Locust when:
- developers own the tests and the team works in Python;
- test logic is complex enough that real code beats GUI elements;
- you want a pass or fail exit code in CI with no extra tooling;
- you need many mostly idle users per machine with little memory.
If you are also weighing Gatling and k6, our k6 vs JMeter vs Gatling vs Locust comparison puts all four side by side, and JMeter vs Gatling runs the same kind of test against Gatling.
Running JMeter tests at scale without managing load generators
Whichever tool you pick, scaling it means running and watching load generators. LoadFocus runs uploaded JMeter test plans from 25 cloud regions: you upload the .jmx with its CSV files, set virtual users, duration and ramp-up per run, and LoadFocus starts the machines, spreads the threads across them and shuts them down after the test. It runs JMeter and k6 scripts, not locustfiles.
The Locust alternative page sets out when moving off Locust makes sense and when staying on it is the better answer. For JMeter, see JMeter load testing in the cloud.
Frequently Asked Questions
Is Locust better than JMeter?
Neither is better overall. Locust is better for Python teams who want tests in code, a pass or fail exit code in CI and many idle users per machine. JMeter is better for testers who prefer a GUI, for protocols beyond HTTP, and for high request rates from one machine: on a 10-core laptop against an instant endpoint, JMeter generated 52,591 requests a second to 2,720 from one Locust process with its default HttpUser.
Is Locust faster than JMeter?
Not per machine. One Locust process uses one CPU core. On a 10-core laptop against an instant endpoint, JMeter generated about 19 times more requests per second than one Locust process with the default HttpUser, about 5 times more than one process with the faster FastHttpUser, and about twice as many as four FastHttpUser processes. Locust does use far less memory and fewer threads per user, holding 2,000 waiting users on 12 threads.
Can Locust run JMeter scripts?
No. Locust runs Python locustfiles only, and it cannot read JMeter’s .jmx test plans. Moving from JMeter to Locust means rewriting each test plan as Python classes. Moving the other way means rebuilding the locustfile’s requests, extractors and logic as JMeter elements.
Does Locust have a GUI like JMeter?
Locust has a web UI, but not a test editor. Its browser interface, on port 8089 by default, starts and stops tests and shows live charts and statistics, while the test itself is always written in Python. JMeter’s GUI is where tests are built and edited, and the JMeter documentation recommends non-GUI mode for running load tests.
Is Locust free?
Yes. Locust is open source under the MIT licence, including its master and worker mode for distributed load. JMeter is also free, under the Apache 2.0 licence, including its client-server mode.
Sources
- JMeter samplers, Thread Group and Open Model Thread Group: Apache JMeter User’s Manual, Component Reference, jmeter.apache.org/usermanual/component_reference.html, November 2026.
- JMeter 5.6.3 as the current release: jmeter.apache.org/download_jmeter.cgi; its default heap (
-Xms1g -Xmx1g):bin/jmeterin the 5.6.3 release, November 2026. - Locust 2.47.0 release date, MIT licence and Python 3.11 requirement: pypi.org/pypi/locust/json and the package metadata, November 2026.
- Locust command-line options (
--processes,--master,--worker,--html,--csv,--json,--exit-code-on-error, web UI port 8089):locust --helpandlocust/argument_parser.pyin 2.47.0. - Locust response time rounding:
bucket_response_timeinlocust/stats.py, 2.47.0. Wait time helpers:locust/user/wait_time.py. Contrib users:locust/contrib/. - Locust event hooks, FastHttpUser and distributed load: docs.locust.io, “Event hooks”, “Increase performance with a faster HTTP client” and “Distributed load generation”, November 2026.
- LoadFocus JMeter runs and its Locust comparison: loadfocus.com/jmeter-load-testing and loadfocus.com/alternatives/locust-alternative, read 4 November 2026.
- Test runs: Apache JMeter 5.6.3 (official release, SHA-512 verified, non-GUI, Java 11.0.19) and Locust 2.47.0 (Python 3.12.13, headless), against a local Node.js 23.11.1 test API with 4 worker processes, on an Apple M1 Max with 10 cores, 4 November 2026. Threads were sampled with ps once a second; CPU time and peak memory come from
/usr/bin/time -l. The laptop carried other work during the runs, so compare the tools within a run, not between runs. Timings show patterns, not benchmarks.