CLI tool
six-ddc/plow avatar
six-ddc/plow

six-ddc/plow: an HTTP benchmarking tool with a live web UI

A high-performance HTTP benchmarking tool that includes a real-time web UI and terminal display

4,515 stars154 forksGoApache-2.0

At a glance

What is it?
Plow is a Go HTTP(S) load generator that streams latency histograms and percentiles to a terminal and a browser in real time. It is aimed at engineers who want a benchmark number without waiting for the run to finish.
Who is it for?
Adopt plow when you need a quick, readable latency distribution from a URL and you want to see it while the run is still going: install via Homebrew, Docker or go install, then start with a short -d run against a staging endpoint. Do not adopt it if your scenario needs scripted request sequences, assertions on response bodies, or a coordinated multi-machine load cluster; plow sends one request shape to one URL and stops there.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 156 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What plow is for, and who ends up using it

Plow answers a narrow question: given one URL, how does response latency distribute across a large number of requests? The README describes it as an HTTP(S) benchmarking tool written in Go that runs a specified number of connections concurrently and records summary statistics, an execution-time histogram and percentiles. The audience is backend and platform engineers who already have a service running somewhere and want a latency profile rather than a pass/fail test. That distinction matters. Plow is not a functional test runner and it does not orchestrate scenarios. It takes a single <url> argument, sends requests to it, and reports. If your work is "does this endpoint hold up under 20 concurrent clients", plow is shaped for it. If your work is "simulate a user who logs in, browses, then checks out", it is not, and no amount of flags changes that.

How plow keeps percentile statistics without slowing the benchmark

The design choice that separates plow from a naive loop-and-average tool is in the reporting path. The README states that plow computes histograms and quantiles in real time using stream-based algorithms inspired by prometheus, with low memory and CPU bounds, and that this adds almost no overhead to the benchmark itself. That is the point: a benchmark client that buffers every latency sample and sorts them at the end either holds a large slice in memory or distorts the measurement while it grows. Streaming quantile estimation trades exactness for a bounded cost per sample. The practical consequence is that the numbers you see mid-run are estimates from a streaming algorithm, not a post-hoc exact sort of every observation. For load testing that is usually the right trade, but you should read plow's percentiles as a stable estimate, not as a ledger. The repository layout supports the description: report.go, print.go and charts.go sit alongside requester.go, so the request engine and the statistics/reporting layer are separate files. On the client side, the README is explicit that plow uses fasthttp rather than Go's net/http, citing fasthttp's client comparison. That is a deliberate bet on a non-standard HTTP stack for throughput, and it is also why plow's request behaviour will not always match a client built on net/http.

Installing plow and running a first benchmark

The README lists three install paths: Go, Homebrew and Docker, plus binary and image assets on the releases page. If you have a Go toolchain, the module path is the install command. Note that go.mod targets Go 1.24.0 with toolchain go1.24.4, so an older toolchain will either fetch the newer one or fail depending on your settings.

bash
go install github.com/six-ddc/plow@latest

On macOS or Linux with Homebrew, the formula is published under the same name.

bash
brew install plow

The Docker image is published to GitHub Container Registry and the README's example uses host networking, which matters because the web UI listens on port 18888 by default and host networking avoids a port mapping step.

bash
docker run --rm --net=host ghcr.io/six-ddc/plow

A first real run needs a target. The README's basic example combines a connection count, a request count and a duration, and plow stops at whichever limit is reached first.

bash
plow http://127.0.0.1:8080/ -c 20 -n 10000 -d 10s

According to the README, the run prints a line saying real-time charts are listening on http://[::]:18888, then a summary with elapsed time, request count, 2xx and 4xx counts, RPS and read/write throughput, followed by a statistics block, a latency percentile table and an ASCII histogram. The same output shows the histogram bucketed by latency with bar counts. For a POST with a JSON body, the README gives a file-based form where the body argument starts with @.

bash
plow https://httpbin.org/post -c 20 --body @file.json -T 'application/json' -m POST

One detail worth knowing before you script plow: every flag can also be set through an environment variable with the PLOW_ prefix, so PLOW_TIMEOUT=5s is equivalent to --timeout=5s. That makes container configuration cleaner than long argument lists.

Where plow stops being the right tool

Plow's model is one URL, one method, one body, repeated. The flag list confirms it: there is -m for the method, -H for headers, -b/--body for the payload, and --stream to decide whether a body file is read into memory or sent with chunked encoding. There is no notion of a request sequence, a variable extracted from one response and fed into the next, or an assertion on a response body. The summary counts 2xx and 4xx responses, which tells you a status distribution but not whether the JSON was correct. If your test depends on server state changing between requests, plow will hammer the same endpoint with the same input, and the results describe that behaviour only. The other boundary is measurement attribution. A load generator consumes CPU on the machine running it. The README does not document a way to separate client-side saturation from server-side saturation, so a flat RPS line at high -c may mean the target is at capacity or that plow itself is the constraint. go.uber.org/automaxprocs is in the dependency list, which suggests container CPU-limit awareness, but the README does not describe how it affects reported numbers, so treat that as an implementation detail rather than a documented guarantee. Finally, plow is a single-process tool. There is no documented distributed mode for driving load from several machines, so a single host's network path and CPU define the ceiling of what you can measure.

Plow compared with ApacheBench and wrk

The topics list on the repository puts plow next to apachebench and wrk, which is the honest comparison set. ApacheBench (ab) is the long-standing baseline: it ships with many distributions, takes a concurrency and a request count, and prints a table when the run ends. Its output is a fixed report, and you get nothing until the run finishes. Plow's difference is the live path: a web UI served on port 18888 plus a terminal snapshot refreshed every --interval (200ms by default), with the option to emit snapshots as JSON via --json or suppress the realtime view entirely with --summary. wrk takes a different route again, using a scriptable Lua layer for request generation, which is where plow has no equivalent. So the split is roughly: choose wrk when the test needs scripting logic, choose ab when you want the smallest possible dependency and a final table, and choose plow when you want a latency histogram and percentile table you can watch while the load is still running, or machine-readable snapshots as it goes. Plow's --rate flag, which accepts values like 50 or 10/ms, and --ramp-up, which increases concurrency over time, are the two controls that make it more than a fixed-rate flood.

Licence, releases and what maintenance looks like

Plow is Apache-2.0, and the README's licence section points at the LICENSE file in the repository. Apache-2.0 is a permissive licence with an explicit patent grant, which is the usual reason a company can embed a tool like this in internal pipelines without a legal review cycle. That is a general property of the licence text, not legal advice for your situation. On releases, v1.4.0 is dated 2026-04-28, v1.3.2 is dated 2024-12-24 and v1.3.1 is dated 2022-07-28. The gap pattern is worth reading plainly: this is a project that ships when something needs shipping, not on a schedule, and there were roughly two years between v1.3.1 and v1.3.2. The repository is not archived, and the last push was on 2026-04-28. Upgrade cost is low in practice because the interface is a CLI with a stable flag set, and the Go module path github.com/six-ddc/plow is importable if you want the requester or reporting code inside another Go program. The risk is not churn; it is that a fasthttp major bump or a Go toolchain requirement change lands in a release and you pick it up on your next go install. The go.mod toolchain line is the concrete thing to watch if your build environment pins Go versions.

Editorial conclusion

Adopt plow when you need a quick, readable latency distribution from a URL and you want to see it while the run is still going: install via Homebrew, Docker or go install, then start with a short -d run against a staging endpoint. Do not adopt it if your scenario needs scripted request sequences, assertions on response bodies, or a coordinated multi-machine load cluster; plow sends one request shape to one URL and stops there. Before you trust a number, confirm which side of the connection is the bottleneck by watching the -c setting against the server's own CPU, because the README documents no way to separate client cost from server cost.

Frequently asked questions

How do I install six-ddc/plow?

The README lists three routes: go install github.com/six-ddc/plow@latest, brew install plow, or the container image at ghcr.io/six-ddc/plow. Binary and image assets are also published on the releases page.

What port does the plow web UI use?

The --listen flag defaults to :18888, and the README's sample output shows the real-time charts listening on http://[::]:18888. The Docker example uses --net=host so that port is reachable without mapping.

Can plow send a POST request with a JSON body?

Yes. The README's example is plow https://httpbin.org/post -c 20 --body @file.json -T 'application/json' -m POST, where a body starting with @ is read from the file path that follows.

Does plow support setting flags through environment variables?

The README states that flag default values are also read from env variables named PLOW_SOME_FLAG, so PLOW_TIMEOUT=5s is equivalent to passing --timeout=5s.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. six-ddc/plow on GitHub
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/six-ddc-plow.svg)](https://hysenlabs.com/projects/six-ddc-plow)