Locust’s headless mode lets you run load tests programmatically, which sounds simple, but the real magic is how it forces you to decouple your test execution from your local machine’s state.
Let’s see it in action. Imagine you have a locustfile.py like this:
from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
wait_time = between(1, 5)
@task
def index(self):
self.client.get("/")
@task(2) # This task runs twice as often
def about(self):
self.client.get("/about/")
To run this in headless mode, you’d typically use a command like this in your terminal:
locust -f locustfile.py --headless --users 100 --spawn-rate 10 --run-time 5m --host http://localhost:8080
Let’s break down what’s happening and why it matters.
The core problem Locust headless mode solves is reproducibility and automation. Running tests manually through the web UI is great for exploration, but for CI/CD pipelines, scheduled tests, or just ensuring the exact same test runs every time, you need a non-interactive method. Headless mode provides this by letting you specify all the necessary parameters directly on the command line.
Internally, when you run in headless mode, Locust bypasses the web server that normally serves the UI. Instead, it directly starts the configured number of users (--users), spawning them at the specified rate (--spawn-rate), and continues for the defined duration (--run-time). The test results, while not visually displayed in a browser, are still collected and can be accessed programmatically or via a separate reporting mechanism.
The key levers you control are:
--headless: This flag is the switch. It tells Locust not to start the web UI.-f locustfile.py: Specifies which Python file contains your user behavior.--users N: Sets the total number of concurrent users to simulate.--spawn-rate N: Determines how many users are started per second. A higher rate means users will ramp up faster.--run-time T: Defines how long the test should run (e.g.,5mfor 5 minutes,1hfor 1 hour). If omitted, the test runs until manually stopped (Ctrl+C).--host URL: The base URL of the application under test. This is crucial for directing your simulated traffic.--reset-stats: Often useful in headless mode. It clears any previous statistics before starting a new test run, ensuring your current run’s results are clean.
When you run locust -f locustfile.py --headless --users 100 --spawn-rate 10 --run-time 5m --host http://localhost:8080, Locust initializes its core components without the web UI process. It sets up the distributed runner (even if it’s just a single process, it uses the same logic), and begins the process of instantiating WebsiteUser instances. The --users 100 means it aims for 100 concurrent users. The --spawn-rate 10 means it will start up to 10 users every second until the target of 100 is reached. The --run-time 5m tells the runner to keep the test going for exactly five minutes from the moment the first user starts executing tasks. After five minutes, Locust will stop spawning new users and wait for the remaining active users to finish their current tasks before reporting completion.
A common pitfall when using headless mode for automated reporting is forgetting that Locust, by default, doesn’t output much to standard out beyond startup/shutdown messages. To get actual results, you’ll often chain it with other tools or use Locust’s built-in reporting capabilities. For instance, you might pipe the output to a file and then parse it, or use the --csv flag to generate CSV reports.
locust -f locustfile.py --headless --users 50 --spawn-rate 5 --run-time 2m --host http://localhost:8080 --csv=my_test_report
This command will run the test and, upon completion, generate my_test_report_stats.csv, my_test_report_failures.csv, and my_test_report_requests.csv in the current directory, which you can then feed into your reporting pipeline.
The next step after mastering basic headless execution is integrating it into a full CI/CD pipeline, which involves handling test artifacts and defining success/failure criteria programmatically.