Engineering

Introducing Pummel

Jul 13, 2026 · 5 min read · David Johnson

A multithreaded web traffic simulation library and CLI written in Rust.

  • Engineering
  • Rust
  • Open Source
On this page

Why Pummel

There are several popular HTTP load testing libraries available today like Locust or Artillery but I wanted a tool that I could use as either a library to include and use with my existing test frameworks or as a standalone CLI that I could invoke from a standard CI/CD process. When looking at the existing rust ecosystem I didn’t find any popular contenders in the space. That led me to start working on Pummel, so I could hit my web servers with traffic patterns that mimicked real user workflows.

What it does

Pummel essentially allows you to define your test scenarios and traffic patterns in TOML/YAML files and constructs a DAG (directed acyclic graph) for task execution. Then the library spins up a multithread execution engine which processes the test scenarios in accordance with the configuration. It’s entirely config driven and exposes capabilities for synthetic data generation and templating to assist creating uniquely generated content that fits the contract of your API.

How it works

At runtime, Pummel turns your config into a dependency graph and executes it with many virtual users in parallel. A single YAML or TOML file defines globals, scenarios, steps, and optional data sources, HTTP client settings, metrics, telemetry, and CI thresholds. Steps declare dependencies; Pummel builds a directed acyclic graph from those edges and rejects cycles at validation time. Unknown fields are denied, so typos fail loudly before you generate load. You can also inspect the graph without running anything:

pummel --config scenario.toml --graph

That prints the DAG as Mermaid or Graphviz DOT and exits.

Each virtual user is a Tokio task that repeatedly runs the scenario until the sustained-load deadline (start + ramp_up + duration). Set duration_seconds = 0 for a single pass — useful as a smoke test. Within each pass, steps whose dependencies are already satisfied form a ready set and run concurrently; dependents wait until that batch completes. think_time_ms adds closed-loop pacing between iterations, while target_rps caps aggregate request-attempt starts for open-loop load; both can be used together.

Dynamic scenarios are where workflow fidelity shows up. You can bind CSV or JSON fixtures per virtual user, sequentially, or at random; render request templates; extract values from responses; and feed them into later steps — without writing a scripting language. A login flow looks like this:

[data_sources.users]
type = "csv"
path = "fixtures/users.csv"
access = "per_vu"

[scenarios.auth_flow]
name = "Authenticated Flow"
steps = ["login", "profile"]

[steps.login]
name = "Login"
method = "POST"
url = "https://api.example.com/login"
json = '{"username":"{{data.users.username}}","password":"{{data.users.password}}"}'

[[steps.login.extractors]]
name = "token"
json_path = "$.token"

[steps.profile]
name = "Profile"
dependencies = ["login"]
url = "https://api.example.com/profile/{{data.users.username}}"

[steps.profile.headers]
Authorization = "Bearer {{token}}"

Built-in helpers like {{vu.id}}, {{uuid}}, and {{random.int:min:max}} cover the rest of the synthetic data you usually need. More detail lives in the dynamic scenarios docs.

You can drive all of this from the CLI or embed it as a library. The CLI supports --dry-run validation, --format json results on stdout, and stable exit codes (0 pass, 1 error, 2 threshold breach) so CI can gate on error rate or latency. As a library, you construct an Engine, run scenarios from Rust integration tests, and assert on metrics directly — the dual-mode story that started the project.

What’s next

Pummel 0.1.0 is usable today as a library and CLI: config-driven HTTP load, dynamic scenarios v1, DAG visualization, newline-delimited JSON telemetry, and CI-friendly thresholds. It ships from source and via signed install scripts on GitHub; it is not on crates.io yet.

The near-term focus is finishing the open-source launch. That means GitHub-first distribution with signed release artifacts across linux-gnu, macOS (x86_64 and aarch64), and Windows MSVC; install scripts that verify checksums with minisign; and an API stabilization pass alongside documented support for the current Rust 1.96.1 MSRV. Once those channels are solid, the next distribution targets are crates.io, an optional Homebrew tap, a Docker image, and winget.

On the product side, OpenTelemetry and Prometheus exporters are not implemented; validation fails if either is selected while telemetry is enabled. They are high on the list. Dynamic scenarios v1 deliberately skips scripting, weighted flow selection, and rich JSONPath; a v2 pass will grow those capabilities carefully. Broader architecture targets — aarch64 Linux and aarch64 Windows — are also on the matrix after the initial launch.

If you want to try it, clone pummel on GitHub, run the install script from the README, or add it to a Rust test harness with a path or Git dependency. Issues and feedback are welcome — Pummel is built for real CI pipelines and embeddable load assertions in the same language as the service under test.