Fortio: a Go load generator that is also Istio's test server
Fortio load testing library, command line tool, advanced echo server and web UI in go (golang). Allows to specify a set query-per-second load and record latency histograms and other useful stats.
At a glance
- What is it?
- Fortio runs a target QPS, records a latency histogram and computes percentiles, and ships an echo server, TCP proxy and web UI in the same binary. Here is what the repository documents, where it stops, and how it compares with k6.
- Who is it for?
- Adopt Fortio if you need a small Go binary that both generates load at a fixed QPS and acts as the echo or proxy target, or if you are already testing Istio and want the tool that project started with. Do not adopt it if you need a scripting-first test suite with checks and thresholds, because Fortio's model is a single URL or gRPC target with a histogram, not a scenario language.
- 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 10 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 October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Fortio solves: fixed-QPS load with a latency histogram attached
Most load tools answer one question badly: how did latency behave as load increased. Fortio was built to answer it directly. The README states that Fortio runs at a specified query per second and records a histogram of execution time, then calculates percentiles such as p99, meaning the response time under which 99 percent of requests completed, expressed in seconds as an SI unit. That framing matters. The output is not a pass or fail, it is a distribution.
The audience follows from that. The project started as Istio's load testing tool and graduated to its own project in 2018, so the first users were people testing a service mesh and needing to know how much latency the mesh added at a given request rate. Today the same shape suits anyone benchmarking an HTTP or gRPC endpoint: platform engineers, SREs checking a latency budget before a rollout, or library authors who want a repeatable number rather than a feeling.
There is a second, less obvious use. Fortio also ships server-side features described as similar to httpbin: request echo including headers, latency or error codes injected with a probability distribution, TCP echoing, TCP proxying, an HTTP fan out and scatter-gather proxy, and gRPC echo and health. That means one binary can be both ends of the test, which removes the usual problem of a benchmark target that behaves differently from the thing you actually run.
How the load generator, server and reporting pieces fit together
The repository is split by function rather than by layer. Top-level entries include fhttp for HTTP client and server code, fgrpc for gRPC, fnet for networking, stats and histogram for the statistics and the latency histogram, tcprunner and udprunner for the TCP and UDP paths, rapi for the REST API, metrics, ui for the web interface, and cli and log for shared command-line and logging helpers.
The command surface mirrors that split. The load subcommand is the HTTP or gRPC load generator that gathers statistics. The server command starts HTTP and gRPC ping servers plus the web UI, result graphing, TCP and UDP echo, proxies and an HTTPS redirector. The grpcping command issues gRPC ping messages. The curl command fetches a single URL for debugging, and the nc command opens a single TCP, Unix domain or UDP connection, with udp:// as the prefix for UDP. There is also a redirect command and a tcp-echo command if you want only that piece.
Results flow in one direction and can be replayed. The documentation states that if you saved JSON results, from the web UI or directly from the command line, you can browse and graph those results with the report command. That is the part worth internalising: the JSON file is the artifact, and the console output is a convenience. The server exposes a REST API to trigger runs and view graphical results, both a single latency graph and comparative graphs of min, max, avg, qps and percentiles across multiple results.
One more mechanism is worth flagging because it is easy to miss. Fortio embeds the grol scripting language and exposes it through fortio script, so interactive fortio.load() scripts can be run from a file. The repository ships scripting_example.gr, and the README shows a run ending with a ramp-up message. That is a different testing model from the flag-driven load command, and the documentation does not reconcile the two in one place.
Installing Fortio from the Docker image, Homebrew or source
The fastest path is the multi-architecture Docker image published as fortio/fortio, built for linux/amd64, linux/arm64, linux/ppc64le and linux/s390x. The README gives this example, which starts the server on ports 8080 and 8079 and then runs a load test against a public URL with colour forced instead of the default JSON log output.
docker run -p 8080:8080 -p 8079:8079 fortio/fortio server &
docker run fortio/fortio load -logger-force-color http://www.google.com/After `fortio server` is running, the README says the web UI is reachable at http://localhost:8080/fortio/. The Dockerfile confirms the port layout: it exposes 8078, 8079, 8080 and 8081, with the default command starting server mode, described in a comment as grpc ping on 8079, HTTP echo and UI on 8080, and the redirector on 8081.
Installing from source needs Go 1.18 or later according to the README, though the repository go.mod declares go 1.25.0, so the practical floor for building the current tree is the go.mod value rather than the README line.
go install fortio.org/fortio@latest
fortio versionThe binary lands in your Go bin directory, usually ~/go/bin. On macOS, Homebrew is supported with `brew install fortio`. On Debian and RPM systems the releases page carries packages, and the README shows both a dpkg and an rpm invocation against version 1.75.2 assets, plus a tarball piped into `sudo tar -C / -xvzpf -`. On Windows the README points at the release zip, extracting fortio.exe, and running `fortio.exe server` from the Command Prompt, allowing the firewall prompt.
For a first real use, run the server locally and point a load run at it. The README's scripting example initialises a URL variable, which is the shape you want for a repeatable local test.
fortio server &
fortio load -qps 100 -t 30s http://localhost:8080/echoWhat you should see is a run that holds roughly the requested rate for the requested duration and prints a latency histogram with percentiles, followed by a summary. Treat the console output as a preview and save the JSON if you intend to compare runs later, because the report command is what turns those files into graphs.
Where Fortio is the wrong tool, and what it does not document
Fortio's model is a target and a rate. If your test needs conditional logic, multi-step user journeys, per-request assertions with thresholds that fail a build, or data correlation across requests, the flag-driven load command will not express it. The grol scripting path is the escape hatch, but the README presents it as an embedded language rather than as a test framework, and there is no documented assertion or threshold vocabulary alongside the load subcommand.
There are also gaps in what the repository states. The README does not document rollback, nor does it describe how to upgrade between releases beyond pointing at the releases page and the package files. The Dockerfile pins a build image by digest, which is good for reproducibility, but it also means the build path is coupled to that image and to the Makefile target official-build-version. The Makefile itself notes that only go1.8 needed a vendor exclusion and that the project has moved through several Go versions, which is a hint that the build has accumulated history.
One more boundary: Fortio measures what the client observes. It will tell you the response time distribution your load generator saw, including any queueing on the generator's own machine. It does not tell you why a percentile moved. And because the server component deliberately injects latency and error codes with a probability distribution, it is easy to build a test that measures your own fault injection rather than the system under test. That is a feature, but it is also a way to produce a confident and meaningless graph.
Fortio versus k6: fixed rate and histogram against scripted scenarios
The comparison people ask about is k6, and the difference is structural rather than a matter of features. Fortio is written in Go, ships as a binary or a small Docker image, and its primary abstraction is a target plus a rate plus a duration or call count. You describe the load with flags and you read a histogram. k6 is a scripting-first tool: tests are written as JavaScript scenarios with virtual users, checks and thresholds, and the tool is designed around expressing user behaviour over time.
That produces different failure modes. With Fortio, the hard part is usually the target and the load generation capacity, and the reporting is essentially free once the run finishes. With a scripted tool, the hard part is the scenario model, and the reporting is where you spend your time. If you want a number for a single endpoint in a CI job, Fortio's shape is a better fit. If you want to assert that 95 percent of a checkout flow completes under two seconds, you want the scenario model.
There is a second axis where Fortio is unusual. Because the same binary provides echo, TCP proxying and scatter-gather, you can stand up the target as well as the load. A scripted load tool generally assumes the target exists elsewhere. For testing a proxy or a mesh sidecar in isolation, that combination is the reason to pick Fortio over a pure load generator.
Licence, dependencies and the real cost of keeping Fortio current
Fortio is licensed under Apache-2.0, which permits commercial and closed-source use and requires that you preserve the licence and notices for the parts you redistribute. If you only run the binary or the Docker image, the obligation is minimal; if you embed the library packages such as stats, fhttp or jrpc into your own product, you are redistributing Apache-2.0 code and should handle attribution accordingly. This is a description of the licence text, not legal advice, and your counsel should review any redistribution.
The dependency surface is worth knowing before you embed anything. The module requires fortio.org/cli, fortio.org/dflag, fortio.org/log, fortio.org/scli, fortio.org/version, golang.org/x/net, google.golang.org/grpc and grol.io/grol, among others, with struct2env, terminal, fsnotify and others pulled in indirectly. Those fortio.org packages are maintained in separate repositories, so an upgrade of Fortio can move several libraries at once.
Upgrade cost is therefore not just the Fortio version. The go.mod declares go 1.25.0, so the build toolchain floor moves with the project. The README claims the Docker image download is under 6Mb with minimal dependencies, and the Dockerfile builds from scratch with only the binary copied in, which is consistent with that claim. For a CLI user, upgrading is pulling a new image or a new package. For an embedder, it is a dependency review.
Editorial conclusion
Adopt Fortio if you need a small Go binary that both generates load at a fixed QPS and acts as the echo or proxy target, or if you are already testing Istio and want the tool that project started with. Do not adopt it if you need a scripting-first test suite with checks and thresholds, because Fortio's model is a single URL or gRPC target with a histogram, not a scenario language. Before rolling it out, verify three things: that your target QPS is reachable from the machine running the load, that the Docker image tag you pull matches the release you intend to test, and that you can read back the saved JSON through the report command rather than only the console summary.
Frequently asked questions
What does the name Fortio mean?
The README states that the name comes from the Greek word φορτίο, which means load or burden, and the repository includes a link to a pronunciation audio file.
How does Fortio compare with k6?
Fortio runs at a specified query per second and records a latency histogram with percentiles, driven by flags, and it also provides the echo and proxy server side. k6 is not described in the repository, so the documented difference is that Fortio's model is a target plus a rate plus a duration or call count rather than a scripted scenario.
How do I install Fortio on Ubuntu?
The README lists a Debian package asset that can be installed with dpkg, an RPM, and a tarball that can be extracted into the filesystem root with sudo tar. The releases page carries the binaries for many OS and architecture combinations.
Which ports does the Fortio server use?
The Dockerfile exposes 8078, 8079, 8080 and 8081, and its comment describes the default server mode as gRPC ping on 8079, HTTP echo and the UI on 8080, and the redirector on 8081. The README says the web UI is at http://localhost:8080/fortio/ once the server is running.
Can Fortio save results and graph them later?
Yes. The README states that if you saved JSON results from the web UI or directly from the command line, you can browse and graph those results using the report command, and the server UI produces both a single latency graph and comparative graphs of min, max, avg, qps and percentiles.
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/fortio-fortio)