Toxiproxy injects TCP faults over HTTP, and nine of its ten clients live in other repositories
:alarm_clock: :fire: A TCP proxy to simulate network and system conditions for chaos and resiliency testing
At a glance
- What is it?
- Toxiproxy is a Go TCP proxy that lets a test inject latency, bandwidth limits and connection failures into a service through an HTTP client. It is precise about what it changes and silent about what your test still has to prove, and the module you import, the tag you install, and the clients you choose can all be at different versions.
- Who is it for?
- Toxiproxy earns its place in a test suite that already routes service traffic through a configurable port, since latency and down are precise enough to prove a fallback path and require no root. Skip it when your code cannot be pointed at a proxy port, when you need a failure mode the nine documented toxics do not cover, or when a green test would be the only evidence you have, because the tool manipulates connections and cannot tell you whether the page still renders.
- Can I use it commercially?
- Yes. MIT 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 5 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 November 14, 2026, and from our analysis. They are not legal advice.
Star history
- Shopify/toxiproxy12,527 ★ · +171
| Repository | 2026-09-22 | 2026-11-14 |
|---|---|---|
| Shopify/toxiproxy | 12.4k | 12.5k |
171 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Two moving parts: a Go TCP proxy and an HTTP client
Usage splits cleanly in two. This repository holds the TCP proxy written in Go, and a separate client talks to it over HTTP. Your application is configured so every test connection goes through the proxy, and from there the test manipulates the health of those connections with HTTP calls. The proxy itself is built from ./cmd/server and the client from ./cmd/cli, so the repository ships a daemon and a command line tool, plus a Go client in client/. The container image is built FROM scratch, EXPOSEs 8474, and starts with CMD ["-host=0.0.0.0"], which sets the listening host to every interface by default. That default is the first thing to check before you run the image somewhere a colleague can reach port 8474, because the HTTP API is what creates proxies and applies toxics.. The module path in go.mod is github.com/Shopify/toxiproxy/v2 and it declares go 1.27.0.
Latency and down are the two toxics the README exercises
The toxics are named types, and the two demonstrated in the opening are the ones that show the design. Adding 1000ms of latency to a MySQL response looks like this:
Toxiproxy[:mysql_master].downstream(:latency, latency: 1000).apply do
Shop.first # this takes at least 1s
endTaking down every Redis instance looks like this:
Toxiproxy[/redis/].down do
Shop.first # this will throw an exception
endTwo details matter more than they look. The direction is explicit, since downstream attaches the toxic to the response path rather than the request path, so a test that only needs the reply to be slow does not have to delay requests it sends. And `/redis/` is a pattern, not a name, so one call reaches every Redis proxy that exists. The block form keeps the manipulation scoped to the assertion inside it. What `down` costs you is not hidden: the example says the call will throw an exception, so every test that uses it has to be written against a failure path, and an assertion about the happy path cannot be wrapped in one.
Proxies are defined before any connection is made, and only in the test environment
Populating Toxiproxy is a bootstrap step, and its position matters. The example adds this to config/boot.rb, with the note that it goes before any connection is made:
require 'toxiproxy'
Toxiproxy.populate([
{
name: "toxiproxy_test_redis_tags",
listen: "127.0.0.1:22222",
upstream: "127.0.0.1:6379"
}
])Then the test environment gets a client aimed at the proxy port instead of the real one:
TagRedis = Redis.new(port: 22222)So the arrangement is two ports and one environment switch. Everything in the test environment reaches Redis through 127.0.0.1:22222, which Toxiproxy forwards to 127.0.0.1:6379, while any other environment keeps its existing configuration. The consequence is that adopting this changes your application configuration, not just your test: a client constant now carries a port that only means something when a proxy is listening on it, and a test that constructs its own connection will bypass the proxy entirely and prove nothing. This is also why the proxy definition has to run early. A connection opened before populate is already attached to the real server.
The unit test proves one method, and the README admits it
The worked example follows a test-first order that is worth copying. First the failure is written:
test "should return empty array when tag redis is down when listing tags" do
@post.add_tag "mammals"
# Take down all Redises in Toxiproxy
Toxiproxy[/redis/].down do
assert_equal [], @post.tags
end
endIt fails with Redis::CannotConnectError, which the text calls perfect, because it means the proxy really did take Redis down for the duration. Then the method is fixed:
def tags
TagRedis.smembers(tag_key)
rescue Redis::CannotConnectError
[]
endAnd then the boundary of the claim is stated: for full coverage you should also write an integration test that wraps fetching the entire blog post page when Redis is down. That sentence is the honest limit of the whole tool as used here. The unit test proves one rescue clause in one method. It says nothing about the controller, the view, or the page, and a green suite with only that one test can hide a request that times out instead of rendering. Toxiproxy makes the fault reproducible; it does not tell you how many places that fault reaches.
The toxic list is finite: nine types, and the docs name all of them
The documented toxics are latency, down, bandwidth, slow_close, timeout, reset_peer, slicer, limit_data, and packet_loss. Between them they cover slow responses, unavailable connections, constrained throughput, connections cut on close, stalled reads and writes, connection resets, split data, byte limits, and dropped data. Each is applied through the HTTP API, which documents proxy fields and toxic fields separately, with endpoints for populating proxies, and the same calls are available from the command line tool. What the list does not contain is as informative as what it does: there is nothing for name resolution, nothing that emulates a host that silently drops packets without the connection ever being established, and no built-in way to throttle connection attempts rather than bytes on an open one. If your failure mode is one of those, you are outside the tool. The repository root does carry a file named CREATING_TOXICS.md and a toxics/ directory, so adding a type means working in the proxy's own code, not configuring it.
nc needs root and one platform, which is the whole argument for Toxiproxy
The project answers the why another proxy question directly. The ones already out there did not offer the kind of dynamic API needed for integration and unit testing, and Linux tools like `nc` are not cross-platform and require root, which makes them problematic in test, development and CI environments. That is the design brief: fault injection reachable from a test, without privileges, on any machine that runs the CI image. The client list is the proof, and it is worth reading for who maintains what. Shopify ships toxiproxy-ruby and toxiproxy-go, the latter living in the client/ directory of this repository. The rest sit in other people's repositories: toxiproxy-python, toxiproxy.net, toxiproxy-php-client, toxiproxy-node-client, toxiproxy-java, toxiproxy-haskell, toxiproxy-rust, and toxiproxy-elixir. Ten clients, one of them in-tree: toxiproxy-go in client/; toxiproxy-ruby is in a separate Shopify repository. The consequence for you is that the language your team writes in probably has a third party client,..
The v2 module path is current, the newest tag is not
Three dates matter when you pin this. The last push is 2026-10-27, so the code is being worked on. The newest release is v2.13.0, dated 2026-10-14, followed back by v2.12.0 from 2025-03-18 and v2.11.0 from 2024-10-16. And go.mod declares module github.com/Shopify/toxiproxy/v2, so the major version is part of the import path. The same gap shows up in the installation instructions, where the Ubuntu section downloads a package named toxiproxy-2.13.0.deb, matching the newest tag, while the Linux section sends you to the Releases page for the latest binaries and system packages for your architecture. If you pin v2.13.0 in CI, you are behind main by whatever was pushed after 2026-10-14. There is a section on upgrading from Toxiproxy 1.x, so the major version has moved before.
A local release runs race tests, shellcheck and yamllint first
The Makefile shows what maintaining this costs, and it is useful to know before you fork it. The default target is setup build test bench fmt lint. Tests run as $(MALLOC_ENV) go test -v -race -timeout 1m ./..., and there is a special MALLOC_ENV variable computed from uname that sets MallocNanoZone=0 on Darwin with Go 1.17 and patch level below 6. End to end tests build the container and run scripts/test-e2e, then scripts/test-e2e-hazelcast against the proxy under a 20 minute timeout. Linting is not only golangci-lint: it also runs shellcheck over scripts/test-*, shfmt with shell specific flags, and yamllint over the tree. The container build is four commands:
env GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w" -o ./dist/toxiproxy-server-linux-$(ARCH) ./cmd/server
env GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w" -o ./dist/toxiproxy-cli-linux-$(ARCH) ./cmd/cli
docker build -f Dockerfile -t toxiproxy dist
docker run --rm toxiproxy --versionNote the smoke test on the last line, and note that the benchmark target still carries a TODO about needing ulimit -n 10240 sockets, so benchmarks may need a raised open-file limit.
Editorial conclusion
Toxiproxy earns its place in a test suite that already routes service traffic through a configurable port, since latency and down are precise enough to prove a fallback path and require no root. Skip it when your code cannot be pointed at a proxy port, when you need a failure mode the nine documented toxics do not cover, or when a green test would be the only evidence you have, because the tool manipulates connections and cannot tell you whether the page still renders. Before pinning anything, check which version your language client targets against v2.13.0, dated 2026-10-14, since main has moved since with a push on 2026-10-27, and read the -host default before you expose port 8474 anywhere shared.
Frequently asked questions
What is a ToxiProxy?
Toxiproxy is a framework for simulating network conditions, built for testing, CI and development environments. It supports deterministic tampering with connections as well as randomized chaos, and its stated purpose is proving with tests that an application has no single points of failure.
what is toxiproxy
It is a TCP proxy written in Go that a client drives over HTTP. Your application sends its test connections through the proxy, and the test changes their health from outside, which means the fault is injected at the connection rather than mocked inside the code.
how to use toxiproxy
Three steps: install it, populate it with proxy definitions that give each one a name, a listen address and an upstream address, then use it from a client. The example puts the populate call in config/boot.rb before any connection is made and points the test client at the proxy port with TagRedis = Redis.new(port: 22222).
toxiproxy vs other chaotic TCP proxies
The stated reasons for building another one are that existing proxies lacked the dynamic API needed for integration and unit testing, and that Linux tools like nc are not cross-platform and require root, which makes them awkward in test, development and CI environments.
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/shopify-toxiproxy)Star history chart for your README
[](https://hysenlabs.com/projects/shopify-toxiproxy)Was this useful?