drill: a load tester you describe in YAML
Drill is an HTTP load testing application written in Rust
At a glance
- What is it?
- A Rust HTTP benchmarking tool with Ansible-style playbooks, request chaining through variable assignment, and no JVM anywhere near your results.
- Who is it for?
- drill is at its best when your benchmark is a sequence rather than a single endpoint, because the plan format lets one request depend on the response of another and turns a login-and-then-fetch workflow into a file a teammate can read. It also wins on operational cost, since a static binary with rustls and no JVM is a good deal easier to run in CI than a JVM toolchain.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 39 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 28, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The argument is about what you do not have to install
drill describes itself as an HTTP load testing application written in Rust, and the stated goal is narrower and more useful than a benchmark score: to build a really lightweight tool as an alternative to tools that require a JVM and other stuff. That framing explains the rest of the design. The comparison in the repository topics names jmeter directly, and the README's demo is an asciinema recording rather than a screenshot of a dashboard.
The practical consequence is in the install section. The easiest route is a release binary for your platform, and the alternative for anyone with Rust available is cargo:
cargo install drill
drill --benchmark benchmark.yml --statsBuilding from source is a clone and a release build, with no additional toolchain to arrange:
git clone [email protected]:fcsonline/drill.git && cd drill
cargo build --release
./target/release/drill --benchmark benchmark.yml --statsThe dependency note matters more than it looks. Drill uses rustls for TLS, so OpenSSL is no longer required to build or install it, although a working C toolchain and cmake may still be needed on some platforms to compile the TLS backend. Removing an OpenSSL dependency from a load tester is not a small thing, because it is what lets the tool drop onto a build machine without a matching system library.
The dependency list in `Cargo.toml` is short and legible: clap with the cargo feature, tokio with just the time and net features, reqwest with cookies and hickory-dns, serde with derive, `hdrhistogram`, `csv`, `regex`, `rand` and `num_cpus`. Choosing only the tokio features it needs, rather than the full feature set, is consistent with the lightweight goal.
Ansible syntax is the actual interface
Benchmarks are YAML files, and the syntax was inspired by Ansible because, as the README puts it, it is really easy to use and extend. Anyone who has written a playbook already knows the shape: a named list of tasks, each with a verb and a block of arguments.
A plan entry is a name plus one of several actions. The verbs you get are `request` for an HTTP call, `assign` to save a value, `assert` to check one, `include` to pull in another file, and `exec` to run an external command. The named sequence is what makes a plan readable as a scenario rather than a config dump:
- name: Fetch users
request:
url: /api/users.json
- name: Fetch account
request:
url: /api/account
assign: fooThe top-level keys are equally short: `concurrency` for the number of concurrent iterations, `iterations` for how many times the whole plan runs, `rampup` in seconds for how long drill takes to start all iterations, and `base` for the server. That is the entire performance envelope, and it is enough to answer the question drill was built for.
The full syntax is not in the README. It lives in `SYNTAX.md` at the repository root, which the README points to for the benchmark file grammar, with runnable examples under `example/` referenced from the feature list for delay, headers, iterations and cookies.
Chaining requests is the part that separates it from a URL list
The feature drill is genuinely good at is request dependency. A task can `assign` its response to a name, and a later task interpolates that name into its URL or body, so a benchmark can log in, take a token out of the response and use it, exactly as a real client would.
The interpolation syntax is Jinja-like, using double braces, and the README shows it reaching into the response body, into the status code, and into headers:
- name: Fetch manager user
request:
url: /api/users/{{ foo.body.manager_id }}
- name: Assert request response code
assert:
key: foo.status
value: 200Because assertions are part of the plan rather than something you inspect afterwards, a benchmark can fail because the application returned the wrong status, not merely because it was slow. That is a meaningful difference for CI use, where a load test that passes against a broken endpoint is worse than no load test at all.
The same templating reaches beyond HTTP. `{{ EDITOR }}` interpolates an environment variable, so one plan file can target different environments without templating it. The `exec` action runs a shell command and assigns its output, which is how the README's example pipes a JSON field through jq and tr to pull out a single value. Headers interpolate the same way, so an Authorization header can carry a value extracted earlier in the plan.
Bulk data, request bodies and session handling
Three features decide whether drill can model a realistic workload rather than a toy one, and all three are documented with worked examples in the benchmark file.
The first is CSV data. `with_items_from_csv` reads a file and builds one request per row, filling interpolations with the values, with options for the file name, a `quote_char` override and a `shuffle` flag. Rows can be objects rather than scalars, so `{{ item.id }}` addresses a column. `with_items_range` generates values numerically with `start`, `step` and `stop`, and `pick` selects a random subset, which is how you get a realistic mix rather than a monotonic sweep.
The second is request bodies. Beyond a string, the README shows two structured forms: a `hex` body given as a byte sequence for binary payloads, and a `file` body that loads content from disk, used in the example to post an image. Method support covers GET, POST, PUT, PATCH, HEAD and DELETE, and headers are set the same Ansible way.
The third is cookies. The feature list describes benchmarks with sessions where cookies propagate between requests, and the release history shows why that needed care: version 0.9.0 includes a fix to stop overwriting old cookies on Set-Cookie, which is the kind of bug that silently breaks any login-based scenario while leaving simple benchmarks green.
Tags are the fourth piece and they are the one the README does not finish explaining, since the feature list is cut off mid-sentence at tags. Since the statistics output groups results, tags are the mechanism for separating a login request from the queries that matter, and the shipped example uses `tag_user`.
Thresholds and the measurement change in 0.9.1
The feature that makes drill usable in a pipeline is thresholds: compare the current benchmark against a stored session and fail if a threshold is exceeded. That is regression detection on latency, which is what distinguishes a load tester that gates a deployment from one that produces a chart someone looks at afterwards.
Version 0.9.1, published on 2026-06-17, changed what latency means, and the release note is unusually blunt about it. Drill now reads the entire response body before stopping its timer, so reported latency reflects time-to-last-byte, matching wrk, k6 and vegeta. Previously the timer stopped at the response headers, which under-reported any endpoint serving a non-trivial body.
The consequence for anyone with historical numbers is direct: expect latencies for body-heavy endpoints to go up, because they are now correct. The same release fixes partial-body downloads, raises the histogram maximum from 3.6 seconds to 1 hour so long-running plans are not clipped, and adds a maximum time per request to the statistics output.
That release also migrates the project to the Rust 2024 edition, updates all dependencies, and fixes npm vulnerabilities in `example/server`. The `Cargo.toml` confirms the edition field and pins version 0.9.1. Statistics are computed with `hdrhistogram`, and the high-resolution timing implied by a one-hour ceiling is what makes percentile reporting meaningful on long runs.
Version 0.9.0 earlier in the year tells the other half of the story. It replaced yaml-rust with serde_yaml, added the `file` and `hex` body types, allowed `$`-prefixed keys during interpolation, added `pick` for `with_items_range`, added a docker-compose file under `example/server`, and took a stream of dependabot bumps.
What the repository does not try to be
The tree is small and tells you the scope directly: `Cargo.toml`, `Cargo.lock`, `LICENSE`, `README.md`, `SYNTAX.md`, `.rustfmt.toml`, and then `src/` plus `example/`. There is no GUI crate, no distributed mode, no plugin system and no reporting server. Whatever drill is, it is a command line tool that reads YAML and writes statistics.
That narrowness is the feature when you compare it to the JVM tools it positions against, and it is a real limitation otherwise. JMeter's GUI and ecosystem exist for people building complex thread groups in a browser; drill's answer is that the YAML file is more editable than a GUI and reviewable in a pull request. Whether that trade works depends entirely on whether your team reviews its benchmarks as code.
The licence is GPL-3.0, which is worth noting because it is a meaningful constraint for a tool embedded in a commercial CI pipeline. The `Cargo.toml` credits Ferran Basora as author and lists ansible and jmeter among the keywords, which is a candid description of the lineage.
With 2,310 stars, 117 forks and 39 open issues, plus a last push on 2026-09-03, this is a maintained project on a pre-1.0 version number. Pre-1.0 is the thing to keep in mind: the measurement semantics changed under you in 0.9.1, so pin the version in CI if you have stored baselines to protect.
Editorial conclusion
drill is at its best when your benchmark is a sequence rather than a single endpoint, because the plan format lets one request depend on the response of another and turns a login-and-then-fetch workflow into a file a teammate can read. It also wins on operational cost, since a static binary with rustls and no JVM is a good deal easier to run in CI than a JVM toolchain. What it does not try to be is a distributed load generator or a rich GUI, and the README does not describe either. Last push was 2026-09-03, and version 0.9.1 changed how latency is measured, so results recorded before that release are not comparable with results recorded after it. Start with a small `benchmark.yml` against a local server, read `SYNTAX.md` for the plan grammar, and calibrate your thresholds against a run you trust before wiring them into a pipeline.
Frequently asked questions
What is drill used for?
drill is a command line HTTP load testing tool. You describe the requests in a YAML benchmark file, and it runs them with a configured concurrency, iteration count and ramp-up, then reports latency statistics and can fail when a stored baseline is exceeded. Because drill is written in Rust it needs no JVM.
How do you install drill?
Download the binary for your platform from the latest release page, or use cargo if you have Rust available. Building from source means cloning the repository and running a release build, after which the binary is at `target/release/drill`. drill uses rustls for TLS, so OpenSSL is no longer required.
Can drill send requests that depend on earlier responses?
Yes, through assign and interpolation. A task can assign its response to a variable, and later tasks reference it with double-brace interpolation into URLs, headers or bodies, including reaching into the response body and status. Assertions can check assigned values during the plan rather than only inspecting results afterwards.
Does drill support CSV test data and cookies?
Yes. `with_items_from_csv` reads a file and builds one request per row with column access such as `{{ item.id }}`, with options for quote character and shuffling, and `with_items_range` plus `pick` generate or sample values. Cookie support propagates cookies between requests so a login-based scenario holds a session.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/fcsonline-drill)