Developer Tools Azure

Locust on Ubuntu 24.04 on Azure User Guide

| Product: Locust on Ubuntu 24.04 LTS on Azure

Overview

This image runs Locust 2.45.0, an open source developer load testing tool, on Ubuntu 24.04 LTS. With Locust you describe how simulated users behave in a small Python file, then swarm thousands of those users at a target system and watch response times, throughput and failures live in a web dashboard and a matching JSON stats API.

The cloudimg image is secure by default. Locust's own web UI ships with no authentication, so this image runs the Locust server bound only to the loopback interface and fronts it with nginx on port 80 using HTTP Basic authentication. The password is a unique value generated on first boot for each instance and written to a root only file. No credential is baked into the image, and no two deployments share one.

What is included:

  • Locust 2.45.0 installed into a pinned, self contained Python virtual environment at /opt/locust/venv
  • A dedicated non root locust service (locust-web.service) bound to 127.0.0.1:8089
  • nginx on port 80 reverse proxying the web UI and stats API with HTTP Basic authentication, plus an unauthenticated /healthz endpoint for Azure Load Balancer probes
  • A one shot locust-firstboot.service that generates a per instance password on first boot and writes it to /root/locust-info.txt (mode 0600)
  • A ready to run sample scenario at /opt/locust/locustfile.py, so you can start a test immediately
  • A fully patched Ubuntu 24.04 LTS base with unattended security upgrades enabled
  • 24/7 cloudimg support

Locust is published by the Locust project under the MIT licence. This image is packaged by cloudimg and is not affiliated with or endorsed by the Locust project.

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting size for driving load from a single node; scale up for higher user counts. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web UI. Locust serves plain HTTP on port 80; for production or shared access, terminate TLS in front of it with your own domain using a reverse proxy or Azure Application Gateway.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Locust by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name locust \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name locust --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

Locust runs as locust-web.service on the loopback interface, fronted by nginx on port 80. A one shot locust-firstboot.service runs once on first boot to generate the per instance password. Confirm all three units are active, and that nginx is listening on port 80 while Locust stays private on 127.0.0.1:8089:

sudo systemctl is-active nginx locust-web locust-firstboot
sudo ss -tlnp | grep -E ':80 |:8089 '

Expected output:

active
active
active
LISTEN 0 128 127.0.0.1:8089 0.0.0.0:* users:(("locust",pid=2459,fd=3))
LISTEN 0 511 0.0.0.0:80     0.0.0.0:* users:(("nginx",...))
LISTEN 0 511 [::]:80        [::]:*    users:(("nginx",...))

Locust listens only on 127.0.0.1, so it is never reachable directly from the network; every request goes through nginx on port 80, which enforces the login.

nginx and locust-web both active, with nginx listening on port 80 and Locust bound to loopback 8089

Step 5 - Retrieve your per instance credentials

The web UI is protected by HTTP Basic authentication. The username is locust and the password is generated uniquely for this instance on first boot and stored in a root only file. Read it with:

sudo cat /root/locust-info.txt

You will see the access URL, the username and the per instance password:

locust_url=http://<vm-ip>/
locust_username=locust
locust_password=<your-unique-password>

The per instance Locust access note recording the URL, the username and the generated password

Keep this password safe. It is unique to this VM; no default password ships in the image. You can rotate it at any time with sudo htpasswd -B /etc/nginx/locust.htpasswd locust.

Step 6 - Confirm the web UI is secure by default

The /healthz endpoint used by load balancer probes is intentionally open and returns 200. The web UI and the stats API, however, require the credentials, so an unauthenticated request returns 401:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/healthz
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/stats/requests

Expected output:

200
401

Supplying the per instance credentials returns the live test state as JSON. Replace <locust-password> with the password from /root/locust-info.txt:

curl -s -u locust:<locust-password> http://127.0.0.1/stats/requests

Expected output (an authenticated request returns the live stats; ready or stopped before you start a test):

{"stats": [...], "errors": [], "state": "ready", "user_count": 0, "total_rps": 0.0, "fail_ratio": 0.0, ...}

An unauthenticated stats request returning 401, then an authenticated request returning the live state JSON

Step 7 - Confirm the version and the sample scenario

Confirm the pinned Locust version, the health endpoint, and look at the sample scenario shipped on the image:

/opt/locust/venv/bin/locust --version
curl -s -o /dev/null -w 'healthz HTTP %{http_code}\n' http://127.0.0.1/healthz

Expected output:

locust 2.45.0 from /opt/locust/venv/lib/python3.12/site-packages/locust (Python 3.12.3)
healthz HTTP 200

The image ships a ready to run sample scenario at /opt/locust/locustfile.py: a minimal HttpUser that repeatedly requests the root path of a configurable target host. This is your starting point, edit it or replace it with your own scenarios.

The Locust version, the healthz probe returning 200, and the shipped sample locustfile

Step 8 - Run a load test from the web UI

In a browser, open http://<vm-public-ip>/ and sign in as locust with the per instance password. Locust shows the Start new load test screen: enter the peak number of simulated users, the ramp up rate (users started per second), and the Host to test against, then click START. The Host defaults to the value in the sample scenario and can be changed to any URL you want to load test.

The Locust Start new load test screen with fields for the number of users, ramp up rate and the target host

Once the test is running, the Statistics tab shows a live table: the number of requests and failures per endpoint, median, 95th and 99th percentile response times, average and maximum times, and the current requests per second. The aggregated row summarises the whole test.

The Locust statistics table during a running test showing requests, failures, response time percentiles and requests per second

The Charts tab plots the same data over time: total requests per second and failures per second, response time percentiles, and the number of simulated users, so you can see how the target behaves as load ramps up.

The Locust charts showing total requests per second, failures per second and response time percentiles over the duration of the test

While a test is running you can adjust the number of users and the ramp up rate on the fly from the EDIT button, or stop and start a fresh run at any time.

The Locust edit running load test dialog for changing the number of users and ramp up rate mid test

Writing your own scenarios

Locust scenarios are plain Python. Edit /opt/locust/locustfile.py, or point the service at your own file, to model real user journeys. A minimal example:

from locust import HttpUser, task, between

class WebsiteUser(HttpUser):
    wait_time = between(1, 3)

    @task(3)
    def index(self):
        self.client.get("/")

    @task
    def about(self):
        self.client.get("/about")

After editing, restart the service with sudo systemctl restart locust-web and reload the web UI. You can also run a test headless from the command line, for example sudo -u locust /opt/locust/venv/bin/locust -f /opt/locust/locustfile.py --headless -u 100 -r 10 -t 1m --host https://your-target.example.

Enabling HTTPS for production

Locust serves plain HTTP on port 80 behind nginx. For production or shared access, put your own TLS certificate in front of it. Point a DNS record at the VM's public IP, then terminate TLS with Azure Application Gateway or extend the nginx configuration in /etc/nginx/sites-available/cloudimg-locust with a certbot issued certificate for your domain. Restrict port 80 to your Application Gateway or management network in the NSG once TLS is in place.

Support

Every cloudimg image includes 24/7 support, 365 days a year, by email and live chat with a 24 hour response SLA. Contact support@cloudimg.co.uk with the product name and your Azure region and we will help.