Deployment guide

Locust 2.47.0

Overview

This guide covers launching and using the Locust 2.47.0 on Ubuntu 24.04 AMI from SecureAnyCloud on Amazon EC2. It is one version of the SecureAnyCloud developer and API tools listing on AWS Marketplace. You pick it as the software version when you subscribe and launch.

Locust is an open-source load-testing tool. You describe how a user behaves in a short Python file (a locustfile), choose how many simulated users to run, and give the address of the system to test. Locust sends that traffic while its web UI shows requests per second, response-time percentiles and every failure live.

You can start, change and stop tests from the browser or from scripts, and download the results as CSV or an HTML report. The SecureAnyCloud image locks Locust down.

What you get:

  • HTTPS from the first boot. nginx serves Locust on port 443 with a certificate generated on your instance's first boot. Port 80 redirects to HTTPS.
  • A password unique to your instance, on everything. Locust's own web UI has no login at all, and anyone who can open it can send load at any address. On this image, HTTP Basic Auth is required for the user admin on every page, chart, download and API call. The password is generated on first boot and written to /var/lib/sac/data/locust-credentials.txt (root only). nginx stores only a bcrypt hash of it. No password is baked into the image.
  • Locust itself is not on the network. The Locust web server listens on 127.0.0.1 only and runs on its own, with no distributed master or worker port open. The security group needs only 22, 80 and 443.
  • Another web site cannot start, stop or change your tests. Every action that changes a test (start, stop, reset, edit users) is a POST request in Locust 2.47.0. nginx refuses any POST whose browser Origin is not this instance, so a page you visit while signed in cannot use your login to start a test. Locust's open CORS headers are removed, so no other site can read the results.
  • Load tests cannot reach the instance metadata service. You choose the address a test targets, but Locust and nginx are blocked at the kernel level from the EC2 instance metadata service (IPv4 and IPv6). A test can never read the instance's metadata or role credentials.
  • The test code is protected. The example locustfile /etc/locust/locustfile.py and the configuration file are owned by root. The Locust service runs as its own unprivileged user that cannot change them, so only an administrator decides what code it runs.
  • A pinned, audited install. Locust 2.47.0 and every Python package it uses (39 in total, pip included) are installed from a lock file that pins each package's exact version and checksum. pip-audit finds no known vulnerability in that set.
  • Hardened defaults. Locust runs with no capabilities, a read-only view of the system except its own state directory, and a memory ceiling. nginx and Locust refuse to start until first boot has generated the certificate and the password. nginx drops requests for host names the instance does not serve, and the AMI requires IMDSv2.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Locust 2.47.0 (in the virtual environment /opt/locust/venv, with its web UI), an example locustfile, nginx, Ubuntu 24.04 LTS with Python 3.12, cloud-init and the AWS Systems Manager agent.

Default user: ubuntu. Architecture: x86_64. Recommended instance type: m5.large.

Prerequisites

Before you deploy, you need:

  • An AWS account subscribed to the listing, an EC2 key pair, and a VPC subnet with a route to the internet (or a private route you use for management)
  • A security group that allows TCP 22 (SSH), TCP 443 (Locust) and TCP 80 (redirects to HTTPS) from your own networks only
  • Permission to load-test the system you point Locust at. Only test systems you own or are authorised to test.
1

Launch the instance

In the AWS Marketplace listing, choose Continue to Subscribe, then Continue to Configuration. Pick Locust 2.47.0 on Ubuntu 24.04 as the software version and select your Region. Then choose Launch through EC2.

Choose m5.large. One Locust process uses about one CPU core, so choose a larger instance for heavier tests (see Step 10). Then choose your key pair, a security group as above, and a subnet that can reach the system you want to test.

To launch with the AWS CLI instead, replace the values in angle brackets:

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=locust}]'
2

Connect to your instance

Connect over SSH. The login user for Locust 2.47.0 on Ubuntu 24.04 is ubuntu:

ssh -i <your-key.pem> ubuntu@<public-ip>
3

Check the services

First boot generates the certificate and the password. Then Locust and nginx start. Check the units:

systemctl is-active locust nginx locust-firstboot
sudo test -f /var/lib/sac/data/locust-firstboot.done && echo FIRSTBOOT_DONE

All three units are active, and the first-boot marker is present:

active active active FIRSTBOOT_DONE

Only SSH and nginx listen on the network. Locust's web server (8089) listens on 127.0.0.1 only:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
0.0.0.0:22 users:(("sshd"),("systemd")) 0.0.0.0:443 users:(("nginx"),("nginx"),("nginx")) 0.0.0.0:80 users:(("nginx"),("nginx"),("nginx")) 127.0.0.1:8089 users:(("locust")) [::]:22 users:(("sshd"),("systemd")) [::]:443 users:(("nginx"),("nginx"),("nginx")) [::]:80 users:(("nginx"),("nginx"),("nginx"))
4

Get your URL and password

sudo cat /var/lib/sac/data/locust-credentials.txt

Only root can read the file. It holds the URL, the user name and this instance's password:

# Locust 2.47.0 - generated on first boot by locust-firstboot.service
# These credentials are unique to this instance; none was baked into the image. Only a bcrypt hash
# of the password is stored (nginx). Keep this file secret. LOCUST_URL=https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ LOCUST_USERNAME=admin LOCUST_PASSWORD=<LOCUST_PASSWORD>
#
# Open LOCUST_URL in a browser (self-signed certificate minted on this instance's first boot; # port 80 redirects to https) and sign in with the username and password above. The same # credential protects every page and the JSON API. # Load test scenarios: /etc/locust/locustfile.py (edit with sudo, then:
sudo systemctl restart locust)
# Self-test (a short test against a throwaway local target):
sudo locust-selftest
# Change the name it is served as:
sudo locust-set-hostname <your-dns-name>
# User guide: https://secureanycloud.com/stacks/locust-2-47-0/

The URL uses the instance's public DNS name from EC2 instance metadata. The certificate also names the public and private IP addresses, so https://<public-ip>/ works too.

To serve Locust under your own DNS name, see Step 12.

5

Sign in

Open the LOCUST_URL in your browser. The certificate is self-signed, so your browser asks you to confirm it the first time. Alternatively, install your own certificate (see Step 12).

Sign in as admin with the password from Step 4. The browser remembers the login for the session.

Locust opens on Start new load test.

6

Run your first test

Fill in the form and choose Start:

  • Number of users (peak concurrency): how many simulated users run at once, for example 20.
  • Ramp up (users started/second): how quickly Locust adds them, for example 4.
  • Host: the base address of the system under test, for example https://staging.example.com. The example locustfile requests / on this host.

The Statistics tab shows each request type with its count, failures, response-time percentiles and current requests per second, updated every two seconds. The screenshot shows 20 users against a small sample web server.

The Charts tab plots requests per second, failures, response times and the number of users over the run.

Use Edit to change the user count while the test runs. Use Stop to end it and Reset to clear the statistics. Use Download Data for CSV files and an HTML report. The Failures tab lists every failed request with its error.

The image also has a self-test. It starts a throwaway web server on the instance and runs a short 2-user test against it through nginx with your password. It then reads the statistics back, stops the test, resets the statistics and checks the security settings. It refuses to run while a test of yours is running:

sudo locust-selftest
SELFTEST-OK https 301 + health + 401 noauth + 401 wrong + web UI + 2-user test on 127.0.0.1 (4 requests, 0 failures) + stop + 403 cross-origin POST + 405 GET /stop + reset + 444 unknown host
7

Write your own test

The example locustfile is a starting point. It is Python code that the Locust service runs, so it lives in /etc/locust, owned by root. Only an administrator can change it:

cat /etc/locust/locustfile.py
"""Example load test shipped with the sac Locust image. Each simulated user requests the root path ("/") of the Host you enter in the web UI, waiting 1-3 seconds between requests, and counts a request that gets no answer within 10 seconds as a failure. Nothing is sent until you start a test. Replace this file with your own scenarios: sudo nano /etc/locust/locustfile.py sudo systemctl restart locust This file is Python that the locust service runs, so it is owned by root and only an admin can change it. """ from locust import HttpUser, between, task class WebsiteUser(HttpUser): wait_time = between(1, 3) @task def index(self): self.client.get("/", name="GET /", timeout=10)

Replace it with your own scenario, then restart Locust to load it. This example logs in once per simulated user and then mixes two requests, three to one, against your own application. The Locust documentation covers tasks, wait times, tags and custom clients:

sudo tee /etc/locust/locustfile.py >/dev/null <<'EOF' from locust import HttpUser, between, task class ShopUser(HttpUser): host = "https://<your-domain>" wait_time = between(1, 5) def on_start(self): self.client.post("/login", json={"user": "loadtest", "password": "test-only"}, timeout=10) @task(3) def browse(self): self.client.get("/products", timeout=10) @task(1) def basket(self): self.client.get("/basket", timeout=10) EOF
sudo systemctl restart locust

Keep a timeout on each request, so that a target that does not answer shows up as a failure instead of hanging a simulated user.

Settings such as the log level live in /etc/locust/locust.conf (configuration reference). Edit the file with sudo and restart Locust. The address Locust listens on is fixed in the service, so it always stays behind nginx.

Locust runs from its own Python environment. To use another Python package in your locustfile, install it there with sudo /opt/locust/venv/bin/pip install <package> and restart Locust.

8

Drive Locust from scripts

Everything in the web UI is also an HTTP API on the same address and login:

  • POST /swarm starts or changes a test (form fields user_count, spawn_rate, host and optionally run_time)
  • POST /stop stops a test
  • POST /stats/reset clears the statistics
  • GET /stats/requests returns the live statistics as JSON

CSV files are at /stats/requests/csv and /stats/failures/csv, and the HTML report is at /stats/report. A script may send no Origin header at all. A browser page must come from the instance itself.

This example starts a small web server on the instance as a stand-in for your application. It runs a 5-user test against that server for a few seconds through the API, prints a summary and stops the test. The password is read from the credentials file and passed to curl on standard input, so it never appears in the process list:

sudo bash -c ' F=/var/lib/sac/data/locust-credentials.txt P=$(grep "^LOCUST_PASSWORD=" $F | cut -d= -f2-) H=$(grep "^LOCUST_URL=" $F | sed -E "s#^LOCUST_URL=https://([^/]+)/.*#\1#") api() { printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk --resolve "$H:443:127.0.0.1" "$@"; } mkdir -p /run/demo-site && echo "<h1>demo</h1>" > /run/demo-site/index.html python3 -m http.server 8000 --bind 127.0.0.1 --directory /run/demo-site >/dev/null 2>&1 & S=$! sleep 1 api -X POST -d user_count=5 -d spawn_rate=5 -d host=http://127.0.0.1:8000 -d run_time=60s "https://$H/swarm" \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(\"start:\", d[\"message\"], \"at\", d[\"host\"])" sleep 12 api "https://$H/stats/requests" | python3 -c " import sys, json d = json.load(sys.stdin) a = [s for s in d[\"stats\"] if s[\"name\"] == \"Aggregated\"][0] print(\"state:\", d[\"state\"], \"| users:\", d[\"user_count\"]) print(\"requests sent:\", a[\"num_requests\"] > 0, \"| failures:\", a[\"num_failures\"])" api -X POST "https://$H/stop" | python3 -c "import sys,json; print(\"stop:\", json.load(sys.stdin)[\"message\"])" api -o /dev/null -w "reset statistics -> HTTP %{http_code}\n" -X POST "https://$H/stats/reset" kill $S; rm -rf /run/demo-site '
start: Swarming started at http://127.0.0.1:8000 state: running | users: 5 requests sent: True | failures: 0 stop: Test stopped reset statistics -> HTTP 200

From another machine, call https://<your-locust-host>/swarm and the other routes with the same user and password. For example, use curl -u admin and let curl prompt for the password, or use your CI tool's HTTP client with Basic Auth.

9

Load tests cannot reach the instance metadata service

Locust sends traffic to whatever host you give it, so the image makes sure that can never be the instance itself. Locust and nginx are blocked from the EC2 instance metadata service (169.254.169.254 and fd00:ec2::254) and the Azure wire server address.

A test pointed there sends requests that never get an answer, and each one is recorded as a connection timeout.

To run the same check from the command line through the API, use the following command. It takes about 25 seconds:

sudo bash -c ' F=/var/lib/sac/data/locust-credentials.txt P=$(grep "^LOCUST_PASSWORD=" $F | cut -d= -f2-) H=$(grep "^LOCUST_URL=" $F | sed -E "s#^LOCUST_URL=https://([^/]+)/.*#\1#") api() { printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk --resolve "$H:443:127.0.0.1" "$@"; } api -o /dev/null -X POST -d user_count=2 -d spawn_rate=2 -d host=http://169.254.169.254 -d run_time=60s "https://$H/swarm" sleep 24 api "https://$H/stats/requests" | python3 -c " import sys, json, re d = json.load(sys.stdin) a = [s for s in d[\"stats\"] if s[\"name\"] == \"Aggregated\"][0] print(\"requests sent:\", a[\"num_requests\"] > 0, \"| every one failed:\", a[\"num_failures\"] == a[\"num_requests\"]) e = d[\"errors\"][0][\"error\"] print(\"each request ended with:\", e.split(\"(\")[0], \"-\", re.search(r\"Connection to [0-9.]+ timed out\", e).group(0))" api -o /dev/null -X POST "https://$H/stop" api -o /dev/null -X POST "https://$H/stats/reset" '
requests sent: True | every one failed: True each request ended with: ConnectTimeoutError - Connection to 169.254.169.254 timed out

The block is a kernel-level rule on the Locust and nginx services (Step 11 shows it). It does not depend on the locustfile, so your own tests are covered too.

10

Bigger tests

One Locust process uses one CPU core. For more load, choose a larger instance type (for example m5.xlarge or c5.2xlarge). Watch the instance's CPU while a test runs. When Locust's own CPU is near 100 percent, the response times it reports include its own delay.

The Locust service has a memory ceiling of 80 percent of the instance's memory, so a very large test cannot exhaust the instance. The service restarts if it reaches the ceiling.

You can also run a test without the web UI, for example from a CI job, with locust --headless. Run it inside the same metadata block and as the locust user with systemd-run. This example runs 5 users against a stand-in web server for 5 seconds and prints the summary that Locust writes with --json:

sudo bash -c ' mkdir -p /run/demo-site && echo "<h1>demo</h1>" > /run/demo-site/index.html python3 -m http.server 8001 --bind 127.0.0.1 --directory /run/demo-site >/dev/null 2>&1 & S=$! sleep 1 systemd-run --quiet --wait --pipe --collect -p User=locust -p WorkingDirectory=/var/lib/locust \
  -p "IPAddressDeny=169.254.169.254 168.63.129.16 fd00:ec2::254" \
  /opt/locust/venv/bin/locust -f /etc/locust/locustfile.py --headless -u 5 -r 5 -t 5s \
  --host http://127.0.0.1:8001 --json 2>/dev/null \
  | python3 -c " import sys, json s = json.load(sys.stdin) print(\"headless run:\", [r[\"name\"] for r in s], \"| requests sent:\", sum(r[\"num_requests\"] for r in s) > 0, \"| failures:\", sum(r[\"num_failures\"] for r in s))" kill $S; rm -rf /run/demo-site '
headless run: ['GET /'] | requests sent: True | failures: 0

Locust can also spread one test over several machines (a master and workers). This image runs Locust on its own and opens no master or worker port.

If you set up workers, note that the master's port (5557) has no authentication. Allow it in the security group from your worker instances' private addresses only.

11

Check the security posture from the command line

Without the password, nothing but the health check answers, and port 80 only redirects. Run:

H=$(sudo grep '^LOCUST_URL=' /var/lib/sac/data/locust-credentials.txt | sed -E 's#^LOCUST_URL=https://([^/]+)/.*#\1#') R="--resolve $H:443:127.0.0.1 --resolve $H:80:127.0.0.1" curl -s -o /dev/null -w 'http:// -> %{http_code} %{redirect_url}\n' $R "http://$H/" curl -sk -o /dev/null -w 'web UI, no login -> HTTP %{http_code}\n' $R "https://$H/" curl -sk -o /dev/null -w 'statistics API, no login -> HTTP %{http_code}\n' $R "https://$H/stats/requests" curl -sk -o /dev/null -w 'start a test, no login -> HTTP %{http_code}\n' $R -X POST -d user_count=1 -d spawn_rate=1 "https://$H/swarm" curl -sk -o /dev/null -w 'health -> HTTP %{http_code}\n' $R "https://$H/health" curl -sk -o /dev/null -w 'unknown host -> %{http_code}\n' --resolve "evil.example:443:127.0.0.1" "https://evil.example/" || true
http:// -> 301 https://ec2-<public-ip-dashed>.<region>.compute.amazonaws.com/ web UI, no login -> HTTP 401 statistics API, no login -> HTTP 401 start a test, no login -> HTTP 401 health -> HTTP 200 unknown host -> 000

000 means nginx closed the connection without answering, which is how it treats any host name it does not serve.

With the password, three protections apply:

  • A request to start or stop a test whose browser Origin is another web site is refused (HTTP 403).
  • Every route that changes a test answers only to POST. A link or an image on another site is a GET, which gets HTTP 405.
  • Locust's CORS headers never reach the browser.

Check them with:

sudo bash -c ' F=/var/lib/sac/data/locust-credentials.txt P=$(grep "^LOCUST_PASSWORD=" $F | cut -d= -f2-) H=$(grep "^LOCUST_URL=" $F | sed -E "s#^LOCUST_URL=https://([^/]+)/.*#\1#") api() { printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null "$@"; } api -w "with login -> HTTP %{http_code}\n" "https://$H/" api -w "start a test, other origin -> HTTP %{http_code}\n" -X POST -H "Origin: https://evil.example" -d user_count=1 -d spawn_rate=1 -d host=https://example.com "https://$H/swarm" api -w "stop, other origin -> HTTP %{http_code}\n" -X POST -H "Origin: https://evil.example" "https://$H/stop" api -w "reset, same origin -> HTTP %{http_code}\n" -X POST -H "Origin: https://$H" "https://$H/stats/reset" for r in swarm stop stats/reset user; do api -w "GET /$r -> HTTP %{http_code}\n" "https://$H/$r"; done printf "user = \"admin:%s\"\n" "$P" | curl -K - -sk --resolve "$H:443:127.0.0.1" -D - -o /dev/null -H "Origin: https://evil.example" "https://$H/stats/requests" \
  | grep -ci "^access-control-" | sed "s/^/CORS headers sent: /" || true '
with login -> HTTP 200 start a test, other origin -> HTTP 403 stop, other origin -> HTTP 403 reset, same origin -> HTTP 200 GET /swarm -> HTTP 405 GET /stop -> HTTP 405 GET /stats/reset -> HTTP 405 GET /user -> HTTP 405 CORS headers sent: 0

nginx keeps only a bcrypt hash of the password. The plain text is in the root-only credentials file:

sudo sed -E 's/\$2y\$12\$.*/$2y$12$<bcrypt-hash>/' /etc/nginx/.locust-htpasswd
sudo stat -c '%a %U:%G %n' /etc/nginx/.locust-htpasswd /var/lib/sac/data/locust-credentials.txt
admin:$2y$12$<bcrypt-hash> 640 root:www-data /etc/nginx/.locust-htpasswd 600 root:root /var/lib/sac/data/locust-credentials.txt

The certificate was generated on this instance and names its own addresses:

H=$(sudo grep '^LOCUST_URL=' /var/lib/sac/data/locust-credentials.txt | sed -E 's#^LOCUST_URL=https://([^/]+)/.*#\1#')
echo | openssl s_client -connect 127.0.0.1:443 -servername "$H" 2>/dev/null | openssl x509 -noout -subject -ext subjectAltName
subject=CN = ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, O = sac X509v3 Subject Alternative Name: DNS:ec2-<public-ip-dashed>.<region>.compute.amazonaws.com, IP Address:<public-ip>, IP Address:<private-ip>, IP Address:127.0.0.1, DNS:localhost

Locust and nginx cannot reach the instance metadata service. Locust runs as its own user with no capabilities, can write only to its state directory, listens on 127.0.0.1 only, and cannot change the test code it runs:

for u in locust nginx; do echo "$u IPAddressDeny: $(systemctl show $u -p IPAddressDeny --value | tr ' ' '\n' | sort | xargs)"; done systemctl show locust -p User -p ProtectSystem -p ReadWritePaths -p CapabilityBoundingSet -p NoNewPrivileges grep -E '^Memory(High|Max)=' /etc/systemd/system/locust.service
sudo tr '\0' ' ' < /proc/$(systemctl show -p MainPID --value locust)/cmdline; echo stat -c '%a %U:%G %n' /etc/locust /etc/locust/locustfile.py /etc/locust/locust.conf
sudo -u locust test -w /etc/locust/locustfile.py && echo "locustfile writable by locust" || echo "locustfile is read only for the locust user"
locust IPAddressDeny: 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 nginx IPAddressDeny: 168.63.129.16/32 169.254.169.254/32 fd00:ec2::254/128 CapabilityBoundingSet= User=locust ReadWritePaths=/var/lib/locust ProtectSystem=strict NoNewPrivileges=yes MemoryHigh=70% MemoryMax=80% /opt/locust/venv/bin/python /opt/locust/venv/bin/locust --config /etc/locust/locust.conf --web-host 127.0.0.1 --web-port 8089 755 root:root /etc/locust 644 root:root /etc/locust/locustfile.py 644 root:root /etc/locust/locust.conf locustfile is read only for the locust user
12

Your own DNS name and certificate

Point a DNS record at the instance, then set the name that Locust is served as. This command mints a new self-signed certificate for the name. It updates nginx, the cross-site protection and the URL in the credentials file, and it keeps the name across reboots:

sudo locust-set-hostname <your-domain>

To use a certificate from your own certificate authority instead, copy it over the generated pair and reload nginx. Running locust-set-hostname again replaces it with a self-signed one:

sudo install -m 0644 <your-domain>.crt /etc/ssl/locust/locust.crt
sudo install -m 0600 <your-domain>.key /etc/ssl/locust/locust.key
sudo nginx -t && sudo systemctl reload nginx
13

Change the password or add users

The login is an nginx Basic Auth file. To set a new password for admin, hand it to htpasswd on standard input. printf is a shell builtin, so the password never appears in the process list. Start the line with a space so that most shells keep it out of your history. Then reload nginx:

printf '%s\n' '<new-password>' | sudo htpasswd -i -B -C 12 /etc/nginx/.locust-htpasswd admin
sudo systemctl reload nginx

Running sudo htpasswd -B -C 12 /etc/nginx/.locust-htpasswd <user> without -i prompts for the password instead. Use it with another user name to give a colleague or a CI system its own login. Run sudo htpasswd -D /etc/nginx/.locust-htpasswd <user> to remove a login.

Then update or delete LOCUST_PASSWORD in /var/lib/sac/data/locust-credentials.txt, so that the file does not hold an old password.

nginx checks the login and does not pass it on to Locust.

14

Server components

  • Locust (Python virtual environment): /opt/locust/venv (/opt/locust/venv/bin/locust)
  • Locustfile and configuration: /etc/locust/locustfile.py, /etc/locust/locust.conf (root-owned)
  • Locust service: locust.service (user locust; web UI 127.0.0.1:8089; state /var/lib/locust)
  • Package lock, pip-audit result, licence, build record: /usr/share/doc/sac-locust/
  • nginx site: /etc/nginx/sites-available/sac-locust
  • Basic Auth file: /etc/nginx/.locust-htpasswd (bcrypt)
  • Certificate and key: /etc/ssl/locust/
  • First-boot script: /usr/local/sbin/locust-firstboot.sh
  • Host name helper: /usr/local/sbin/locust-set-hostname
  • Self-test: /usr/local/sbin/locust-selftest
  • Credentials: /var/lib/sac/data/locust-credentials.txt (mode 0600)
/opt/locust/venv/bin/locust --version nginx -v 2>&1 grep -c -- '--hash=sha256:' /usr/share/doc/sac-locust/requirements.lock sed -n '1,6p' /usr/share/doc/sac-locust/BUILD-INFO.txt
locust 2.47.0 from /opt/locust/venv/lib/python3.12/site-packages/locust (Python 3.12.3) nginx version: nginx/1.24.0 (Ubuntu) 39 locust 2.47.0 (PyPI; tag 2.47.0, commit 66279c936b4b69a8a7df3ac3b11f7e47d20780de), MIT wheel sha256 a773af200bb93b1bed0b99c48c18f11343ed159803a8af3ee3ad0696b05b7d09 installed into /opt/locust/venv (CPython 3.12) from requirements.lock: 39 wheels, every one pinned by version and sha256 (pip install --require-hashes --no-deps --only-binary :all:) pip-audit 2.10.1 (build-only, not shipped) on the exact lock: OK 39 packages, 0 known vulnerabilities (pip-audit.json) web UI the compiled static bundle shipped in the wheel (locust/webui/dist: index.html, src-Cl6CiemE.js

/usr/share/doc/sac-locust/requirements.lock lists every installed package with its exact version and checksum, and pip-audit.json holds the audit of that set.

15

Security notes and recommendations

  • Allow ports 22, 80 and 443 in the security group from your own networks only. Anyone with the Locust password can send load from this instance to any address it can reach, so treat the password like a production credential and rotate it if it may have been shared (Step 13).
  • Only load-test systems you own or are authorised to test, and agree the test window with the people who run the target. A load test from an EC2 instance to a public address is also subject to the AWS Acceptable Use Policy and the target's own terms.
  • The locustfile is Python code with the Locust service's permissions on the instance (its own user, no capabilities, no access to the metadata service). Review any locustfile before you install it.
  • Use your own DNS name and a certificate your browsers trust (Step 12).
  • Locust's web UI is a compiled bundle shipped inside the Locust release. Its upstream build tools carry advisories (for example in vite and brace-expansion), but those tools are not part of the files the instance serves. SecureAnyCloud rechecks the bundle and every Python package at each Locust release.
  • Keep the operating system patched. Unattended security upgrades are on, and sudo apt-get update && sudo apt-get upgrade applies everything else.

Support and licensing

Locust is open source under the MIT licence (Carl Byström, Jonatan Heyman, Lars Holmberg and contributors). The licence text is in /usr/share/doc/sac-locust/. Its Python dependencies and web UI are open source under their own permissive licences.

There is no per-user or per-CPU licence fee. The AMI price covers SecureAnyCloud's build, hardening, first-boot setup and support.

  • Email: sales@secureanycloud.com
  • Website: https://secureanycloud.com
  • Support: 24/7, with a 24-hour response SLA

Deploy on AWS

Launch Locust 2.47.0 on Ubuntu 24.04 on AWS with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on AWS