# Locust: load tests written as plain Python, not XML or a GUI

> Locust is an MIT-licensed load testing framework where scenarios are ordinary Python functions running inside gevent greenlets. It is a good fit for teams that want version-controlled tests and distributed runs, and a poor fit for anyone who wants a point-and-click recorder.

**locustio/locust** — Write scalable load tests in plain Python 🚗💨

- Repository: https://github.com/locustio/locust
- Website: https://locust.cloud
- Stars: 28,186 · Forks: 3,251
- Language: Python
- License: MIT
- Published: 2026-09-21 · Updated: 2026-09-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/locustio-locust

## The problem Locust solves for Python teams

Most load testing tools ask you to express user behaviour in a format that is not code: XML, a proprietary binary, or a sequence of clicks in a GUI. That works until a scenario needs a login token computed from a response body, a loop over ten product IDs, or a branch on a feature flag. At that point you are either fighting the tool or writing a plugin in a language you did not choose.

Locust takes the other position. The README states that its developer-friendly approach lets you define tests in regular Python code, and that you can import regular Python libraries into your tests. The repository description puts it as writing scalable load tests in plain Python. The intended audience follows from that: developers and system administrators, which is also what the classifiers in pyproject.toml list as the intended audience.

The practical consequence is tooling. Because a locustfile is a normal Python module, your IDE completes it, your linter checks it, and git diffs it. The README makes this contrast explicit, noting that other tools use XML or binary formats for test definitions.

The second problem is concurrency cost. Locust runs every simulated user inside its own greenlet, a lightweight coroutine. The README says this lets you write tests as normal blocking Python instead of using callbacks. A single process can therefore hold many thousands of concurrent users, which matters when the thing you are testing is concurrency rather than raw request throughput.

## Greenlets, tasks and the master-worker split

A Locust test file declares classes that inherit from HttpUser. The class body carries three things: a wait_time, optional lifecycle hooks such as on_start, and methods decorated with @task. The decorator accepts a weight, so @task(3) means that method is picked three times as often as an unweighted one.

Each simulated user is a greenlet. It picks a task according to the weights, runs it, then sleeps for a duration drawn from wait_time, which in the README example is between(1, 2). Because the greenlet is cooperative, the self.client.get calls look blocking in your source but yield to the event loop while the HTTP request is in flight. That is the whole trick behind running thousands of users in one process.

The client is not limited to HTTP. The README says Locust can test almost any system or protocol, and points at writing your own client or using community ones. The repository ships example directories for grpc, custom_xmlrpc_client and milvus, and pyproject.toml declares optional dependency groups named milvus, mqtt, dns and otel. Those extras are only installed when you ask for them.

For scale-out, Locust runs distributed. The README describes running load tests over multiple machines, and the web UI has a workers view for this. The transport between processes is ZeroMQ and msgpack, both listed as hard dependencies, with Flask and python-socketio serving the browser side. That is a real constraint worth knowing: the web UI is a Flask application, so exposing it broadly is a decision about a web service, not just a test runner.

## Installing Locust and running a first test

The README does not inline the install commands; it points to the documentation installation page. The package name on PyPI is locust, and the project metadata requires Python 3.11 or newer, so create an environment with a suitable interpreter and install the package from PyPI.

```bash
pip install locust
```

After that, locust --version prints the installed version. If it errors, the interpreter is older than the requires-python floor in pyproject.toml.

Next, write a locustfile. The README gives this example, which posts a login in on_start and then runs two weighted tasks:

```python
from locust import HttpUser, task, between

class QuickstartUser(HttpUser):
    wait_time = between(1, 2)

    def on_start(self):
        self.client.post("/login", json={"username":"foo", "password":"bar"})

    @task
    def hello_world(self):
        self.client.get("/hello")
        self.client.get("/world")

    @task(3)
    def view_item(self):
        for item_id in range(10):
            self.client.get(f"/item?id={item_id}", name="/item")
```

The name="/item" argument is worth noticing. Without it, each item_id would create its own statistics row and the report would be unreadable. With it, all ten requests aggregate under one label.

Run the test against a host you control, then set the user count and spawn rate in the web UI. The UI shows throughput, response times and errors in real time, and the README notes you can change the load while the test is running. For CI, the same file runs headless, which the README calls out as making it easy to use without the UI.

## Where Locust is the wrong tool

The README is candid that the project does not try to do everything. It says the code base is intentionally kept small and does not solve everything out of the box, positioning extensibility as the answer instead. Read that as a warning about scope: if you want a tool that records browser traffic and replays it, Locust is not that, and the README explicitly frames its design as an alternative to GUI and domain-specific-language approaches.

The second limitation is the concurrency model itself. gevent gives you cheap users, but cooperative scheduling means a task that blocks without yielding, for example a CPU-heavy computation or a synchronous database driver, stalls every other greenlet in that process. The README acknowledges the trade-off indirectly when it says other tools may be capable of more requests per second on given hardware, and that Locust's advantage is low overhead per user for highly concurrent workloads. If your goal is maximum requests per second from one box, that sentence tells you the design is not aimed at you.

The third is measurement validity. A load generator written in Python and running on the same class of hardware as your service can itself become the bottleneck, and the reported response times will not tell you which side saturated. Nothing in the README claims otherwise, but nothing there helps you distinguish the two either.

Finally, the web UI is a live service. Running it without the UI for CI is documented, and that is the mode to prefer when the generator sits anywhere near production.

## Locust against JMeter and k6

The nearest well-known alternative in the same category is Apache JMeter. JMeter is a Java application with a graphical test-plan editor, and test plans are saved as XML. That is precisely the model the Locust README contrasts itself with. The difference in approach is not cosmetic: a JMeter plan is data you edit in a tool, while a locustfile is source you edit in an editor and review in a pull request. JMeter compensates with a large built-in library of samplers and listeners, so a team that wants breadth without writing code may find it faster to start.

k6 is the closer comparison for people who want code-defined tests. It uses JavaScript rather than Python, and it ships as a single Go binary rather than a Python package with a dependency tree that includes gevent, Flask, ZeroMQ and msgpack. If your team writes Python all day, Locust lets you reuse that language and its libraries inside the test; if your team does not, the Python requirement is a cost with no offsetting benefit.

The honest summary is that the choice hinges on language and on how much behaviour you need to express. Locust's own README argues the case in one line: test design will never be limited by a GUI or a domain-specific language.

## Maintenance, upgrades and the MIT licence

The repository is not archived, and the last push was on 2026-09-17. Releases are frequent and small: 2.46.4 on 2026-08-23, 2.46.5 on 2026-09-07, and 2.46.6 on 2026-09-17. Patch-level versioning at that cadence suggests incremental fixes rather than a long-stable API, so pinning a version in your test dependencies and upgrading deliberately is the safer pattern.

The upgrade surface is wider than a pure Python library. pyproject.toml pins minimums for gevent at 24.10.1, geventhttpclient at 2.3.1, Flask at 2.0.0, pyzmq at 25.0.0 and msgpack at 1.0.0, and it declares support for Python 3.11 through 3.15. A Locust upgrade can therefore move your event loop, your HTTP client and your web server at once. Test files that depend on the exact behaviour of the HTTP client are the ones most likely to need attention.

The web UI is a separate build. package.json defines yarn scripts for webui:install, webui:build and webui:test, and the Dockerfile builds the front end in a node:22.0.0-alpine stage before installing the Python wheel. If you install from PyPI you get the built assets; if you build from source you need both uv and yarn, as the Makefile's check-uv and check-yarn targets enforce.

The licence is MIT, declared both in pyproject.toml and in package.json, with a LICENSE file at the repository root. MIT is permissive and imposes no copyleft obligation on your test code. That is a summary of what the files say, not legal advice; confirm the terms with your own counsel if the distinction matters to you.

## Conclusion

Adopt Locust if your team already writes Python and wants load scenarios reviewed and versioned like application code, or if you need to drive a protocol the tool does not ship a client for. Do not adopt it if you need a record-and-replay GUI, because the README describes the opposite philosophy: the code base is kept small and deliberately does not solve everything out of the box. Before committing, verify that your Python version satisfies requires-python = ">=3.11" in pyproject.toml, and check whether the gevent-based model suits the latency profile you intend to measure.

## FAQ

### How do I install Locust?

Install the locust package from PyPI into a Python 3.11 or newer environment, for example with pip install locust, then confirm it with locust --version. The README itself points to the documentation installation page rather than listing the commands.

### How do I install Locust on Windows?

The README does not give a Windows-specific procedure; it directs readers to the documentation installation page, and pyproject.toml lists pywin32 as a dependency on sys_platform win32. The package is installed from PyPI like any other Python package, so the same pip install locust applies.

### How do I use Locust for load testing?

Write a locustfile containing a class that inherits from HttpUser with a wait_time, an optional on_start hook and methods decorated with @task, then run it with locust -f locustfile.py --host http://localhost:8089. The web UI shows throughput, response times and errors in real time, and the same file can be run without the UI for CI.

## Sources

- [License: MIT](https://github.com/locustio/locust/blob/master/LICENSE)
- [locustio/locust on GitHub](https://github.com/locustio/locust)
- [Project website](https://locust.cloud)
- [README](https://github.com/locustio/locust/blob/master/README.md)
- [Releases](https://github.com/locustio/locust/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/locustio-locust
