The most surprising thing about running Locust in Docker is that it doesn’t automatically handle distributed testing for you; you have to explicitly configure it.
Let’s see Locust in action, running a simple load test against a local web server. First, we need a Dockerfile for our Locust swarm.
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY locustfile.py .
# Expose the Locust web UI port and the distributed mode port
EXPOSE 8089
EXPOSE 5557
EXPOSE 5558
CMD ["locust", "-f", "locustfile.py", "--master"]
And a simple locustfile.py:
from locust import HttpUser, task, between
class WebsiteUser(HttpUser):
wait_time = between(1, 5)
host = "http://webserver" # This will be overridden by Docker networking
@task
def index(self):
self.client.get("/")
@task
def about(self):
self.client.get("/about")
We’ll also need a requirements.txt:
locust
Now, let’s build this and run a single Locust master container.
docker build -t locust-master .
docker run -d --name locust-master-container -p 8089:8089 locust-master
This starts a single Locust master. You can access the web UI at http://localhost:8089. You’ll see it’s running with 0 workers. To get actual load, we need workers.
Here’s how we set up distributed mode. We’ll start a few worker containers.
# Start the first worker
docker run -d --name locust-worker-1 --network host locust --master-host locust-master-container -f locustfile.py
# Start the second worker
docker run -d --name locust-worker-2 --network host locust --master-host locust-master-container -f locustfile.py
Notice the --network host flag. This is crucial for simple setups to allow containers to easily communicate with the Locust master on the host’s network. Alternatively, you’d need to create a custom Docker network and ensure containers can resolve each other by name.
If we didn’t use --network host, we’d create a network:
docker network create locust-net
Then run the master and workers on that network:
# Master on the custom network
docker run -d --name locust-master-container --network locust-net -p 8089:8089 locust-master
# Workers on the custom network, referencing the master by its container name
docker run -d --name locust-worker-1 --network locust-net locust --master-host locust-master-container -f locustfile.py
docker run -d --name locust-worker-2 --network locust-net locust --master-host locust-master-container -f locustfile.py
In the Locust UI, you’ll now see the workers registered. The host in our locustfile.py (http://webserver) is a placeholder. When you start the test, you’ll enter the actual target host in the UI. For example, if your web server is running on http://localhost:5000, you’d enter that in the "Host" field.
The core problem Locust solves is simulating a high volume of concurrent users to stress-test your application. It does this by separating the "master" process, which coordinates the test and serves the UI, from "worker" processes, which actually execute the user behavior defined in your locustfile.py. In a Docker setup, each of these can be its own container, allowing for easy scaling and isolation.
The master orchestrates everything. It listens for connections from workers and tells them what to do (which user class to instantiate, how many users to spawn, etc.). It also aggregates statistics from all workers and displays them in the web UI. Workers are the workhorses; they receive instructions from the master, spawn the specified number of users, and report their metrics back.
The most counterintuitive aspect of this setup is how Locust handles the target host. You might expect to configure the target host within the Dockerfile or via environment variables passed to the master. However, the host parameter in HttpUser is primarily a default. The actual host used for the load test is the one you input into the Locust web UI when you start a test run. This design choice makes the locustfile.py more portable, as it doesn’t need to be tied to a specific deployment environment.
The next step would be to explore how to configure Locust to run on Kubernetes for more robust, scalable distributed load testing.