# go-wrk: an HTTP load generator in Go that prints the numbers wrk prints

> go-wrk is a small Go program that drives HTTP load with goroutines and reports throughput, latency percentiles and errors in the same shape as wrk, which it credits as its inspiration. The code is compact and the dependency list is two libraries, but the project ships no releases and its last commit was in July 2026.

**tsliwowicz/go-wrk** — go-wrk - a HTTP benchmarking tool based in spirit on the excellent wrk tool (https://github.com/wg/wrk)

- Repository: https://github.com/tsliwowicz/go-wrk
- Stars: 1,059 · Forks: 129
- Language: Go
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/tsliwowicz-go-wrk

## A benchmark tool that borrows wrk's interface and owes it the credit

go-wrk describes itself as a modern HTTP benchmarking tool capable of generating significant load on a single multi-core CPU, built on Go's goroutines and scheduler for the asynchronous I/O and concurrency underneath.

The origin story is in the README and it is unusually direct. It was created mostly to examine Go's performance and verbosity compared with C, the language wrk was written in, and it says the result was that go-wrk is just as good in terms of throughput, with a lot less code. The README then states that the majority of the project is the product of one afternoon and that its quality is comparable to wrk.

The acknowledgements section repeats the point and goes further. The author fully credits the wrk project for the inspiration and for parts of the text, and says the same command line argument format and output format were used. So the tool is best read as a Go reimplementation of the wrk interface, not as a different benchmarking philosophy.

The size supports the story. The repository root holds `go-wrk.go`, a `loader/` directory and a `util/` directory, and `go.mod` lists exactly two direct dependencies: `github.com/HdrHistogram/hdrhistogram-go` for the latency histogram and `golang.org/x/net` for the HTTP stack. The module requires Go 1.25.0. The licence is Apache-2.0.

There is no GitHub releases page for the project, so the installation route is the source one, and the last push was on 2026-07-04.

## Build it with one command and the output is the familiar one

The whole installation is:

```bash
go install github.com/tsliwowicz/go-wrk@latest
```

That downloads and compiles the tool, and because the module has two dependencies and no code generation step, it is quick.

A basic run looks like this:

```bash
./go-wrk -c 2048 -d 10 http://localhost:8080/plaintext
```

The two numbers that matter are `-c`, the number of goroutines to use, which is the concurrent connection count, and `-d`, the duration of the test in seconds, which defaults to 10. The example runs a ten second benchmark with 2048 connections.

The output is the part that will make you feel at home if you have used wrk:

```text
Running 10s test @ http://localhost:8080/plaintext
    2048 goroutine(s) running concurrently
439977 requests in 10.012950719s, 52.45MB read
Requests/sec:		43940.79
Transfer/sec:		5.24MB
Fastest Request:	98µs
Avg Req Time:		46.608ms
Slowest Request:	398.431ms
Number of Errors:	0
Error Counts:		map[]
10%:			    164µs
50%:			    2.382ms
75%:			    3.83ms
99%:			    5.403ms
99.9%:		    5.488ms
99.9999%:		    5.5ms
99.99999%:		    5.5ms
stddev:			    29.744ms
```

The percentile ladder is worth reading carefully, because it is where a benchmark hides its problems. The 50th percentile is 2.382ms while the average request time is 46.608ms, and the standard deviation is 29.744ms. That gap between median and average is the signature of a small number of very slow requests dragging the mean, and the tail percentiles out to 99.99999% are printed so that you can see the shape of it rather than the average hiding it.

## The option list is short, and each flag changes what you are measuring

The help output lists a fixed set of flags, and several of them change the meaning of the test rather than just its shape.

`-c` is the concurrency, `-d` the duration, `-T` the socket and request timeout in milliseconds, defaulting to 1000, and `-M` the HTTP method, defaulting to GET. `-H` adds a header to each request and can be given several times, and `-body` takes a request body string or an `@filename`. `-host` sets the Host header separately from the URL, which is what you need when the server routes on a name the benchmark URL does not carry. `-f` names a playback file, and `-redir` allows redirects, which is off by default.

The compression and keep-alive flags are the ones that decide whether the benchmark is realistic. `-no-c` disables compression by preventing the `Accept-Encoding: gzip` header, and `-no-ka` disables keep-alive so that TCP connections are not reused between requests. Both default to off, meaning compression and connection reuse are on by default, which is the right default for a modern service.

The TLS flags are coarse compared with what a general purpose client offers. `-ca` gives a CA file to verify the peer against, `-cert` a CA certificate file, `-key` a private key file, and `-no-vr` skips verification of the server certificate, which is off by default. There is no separate option for a client certificate chain beyond these.

One flag stands out: `-http`, which is described as using HTTP/2 and defaults to true. Since the option is on by default, a run against an HTTP/2 server is the default behaviour, and turning it off is the way to compare the two protocols on the same endpoint.

## The port and backlog advice decides whether your numbers mean anything

The README has a short section called Benchmarking Tips, and it is the most practically important part of the document for anyone taking a result seriously.

It has two requirements. The machine running go-wrk must have a sufficient number of ephemeral ports available, and closed sockets should be recycled quickly. To handle the initial connection burst, the server's listen(2) backlog should be greater than the number of concurrent connections being tested.

Both are failure modes that look like application slowness. If the client runs out of ephemeral ports, connections start failing or queueing and the latency distribution inflates, which reads as a slow server. If the server's accept backlog is smaller than the connection count, the kernel drops or delays the surplus connections during the initial burst, and the first part of the test measures queueing rather than throughput. The tool reports errors in its output, but a backlog overflow can show up as latency rather than as an error count.

This is also why the connection count is worth choosing carefully. The example uses 2048 goroutines, which is a lot of sockets from one machine, and the tips section exists precisely because that number has consequences on both ends of the connection.

## Where go-wrk is the wrong tool

The first wrong case is a scripted workload. The option list is a fixed set of flags with a playback file, and the README does not describe a scripting mechanism for generating varied requests on the fly. A benchmark that needs per-request variation, such as a realistic mix of endpoints with different payloads, is not what this tool does as documented.

The second is a project that needs a maintained release. The repository has no GitHub releases, the recent release list is empty, and the last push was on 2026-07-04. Installing with `go install ...@latest` therefore tracks the default branch, which is convenient and also means you have no version to pin. For a benchmark tool that ends up in CI, that is a real drawback, since a change in the tool changes your numbers without you changing anything.

The third is a workload that is not HTTP over TCP. The tool is an HTTP benchmarking tool and nothing else: no WebSocket support, no raw TCP, no ability to run a script against a protocol it does not understand.

The fourth is precision work at the tail. The histogram comes from `hdrhistogram-go`, and the output prints percentiles down to 99.99999%, which is more detail than most tools show. That is useful, but a histogram is a bucketed approximation, and if you need exact tail behaviour you would look at the latency distribution from the server side instead of from the client.

The last is a codebase that prefers to build on the original. The author states the influence of wrk openly, and for a project that already uses wrk, the gain from switching is a different language for the tool and a different maintenance burden, not a different set of measurements.

## Conclusion

Adopt go-wrk if you want an HTTP load generator you can build from source with a single `go install`, if your team is more comfortable reading Go than C, or if you need wrk-compatible output in a Go toolchain. Do not adopt it as a checked release artefact, because the repository has no GitHub releases and you would be tracking the default branch, and expect only the small fixed option list described above. Before running a benchmark with it, check the ephemeral port availability and the server's listen backlog against your concurrency level, because the README names those as the two reasons a load test produces misleading results.

## FAQ

### How do I install and run go-wrk?

Install it with `go install github.com/tsliwowicz/go-wrk@latest` and run it with a URL, for example `./go-wrk -c 2048 -d 10 http://localhost:8080/plaintext`, where `-c` is the number of concurrent connections and `-d` the duration in seconds.

### What does the go-wrk output report?

It reports requests per second, transfer per second, the fastest, average and slowest request times, the number of errors with an error count map, a percentile ladder from 10% up to 99.99999%, and the standard deviation.

### Does go-wrk use HTTP/2 and keep-alive connections by default?

The help output lists `-http` for using HTTP/2 with a default of true, and `-no-ka` to disable keep-alive with a default of false, so HTTP/2 and connection reuse are on unless you turn them off. There is also `-no-c` to suppress the Accept-Encoding gzip header.

### What should I check before trusting a go-wrk benchmark result?

The README says the client machine needs enough ephemeral ports with closed sockets recycled quickly, and that the server's listen(2) backlog should be greater than the number of concurrent connections being tested, so that the initial connection burst is not what the test measures.

## Sources

- [Issues](https://github.com/tsliwowicz/go-wrk/issues)
- [License: Apache-2.0](https://github.com/tsliwowicz/go-wrk/blob/master/LICENSE)
- [README](https://github.com/tsliwowicz/go-wrk/blob/master/README.md)
- [tsliwowicz/go-wrk on GitHub](https://github.com/tsliwowicz/go-wrk)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tsliwowicz-go-wrk
