Library / SDK
mtrudel/bandit avatar
mtrudel/bandit

Bandit: An Elixir-Native HTTP Server That Replaces Cowboy in Phoenix

Bandit is a pure Elixir HTTP server for Plug & WebSock applications.

1,934 stars130 forksElixirMIT

At a glance

What is it?
Bandit is a pure Elixir HTTP/1.x, HTTP/2 and WebSocket server for Plug and WebSock apps, built on Thousand Island. It is now the default Phoenix server, and its conformance test scores and performance claims are worth checking before you switch.
Who is it for?
Adopt Bandit if you run a Phoenix or Plug app and want a server written entirely in Elixir with strong HTTP/2 and WebSocket conformance, and if you accept a smaller ecosystem than Cowboy's. Skip it if you need exotic endpoint configuration that the Phoenix adapter does not translate, or if you rely on Cowboy-specific features like advanced routing or custom transports.
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 3 days ago.
What is it written in?
Mainly Elixir, 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.

From daily snapshots

Star history

  • mtrudel/bandit1,934 ★ · +32
Star history
Repository2026-07-282026-11-14
mtrudel/bandit1.9k1.9k

32 Stars in the last 109 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

What Bandit Solves and Who It Is For

Bandit solves a specific problem: serving Plug and WebSock applications with an HTTP server that is written in the same language as the app. For years, Elixir developers used Cowboy, an Erlang server, because it was the default in Phoenix. Bandit is a pure Elixir alternative, built on Thousand Island, that aims to be a drop-in replacement. The README states it is the default HTTP server for Phoenix since release 1.7.11. That makes Bandit relevant to anyone running a Phoenix app, but it is also aimed at developers who want to understand the lower layers of their stack. The project goal is to demystify infrastructure code, so the codebase is meant to be approachable. The target user is a Phoenix or Plug developer who wants a server that speaks HTTP/1.x, HTTP/2 and WebSockets natively, and who is comfortable trading the maturity of Cowboy for a more Elixir-centric codebase.

Architecture: Thousand Island and the Delegation to Plug and WebSock

Bandit is built atop Thousand Island, a separate library from the same author. That library handles the low-level networking, and Bandit implements the HTTP semantics. The README says Bandit's goal is to delegate to Plug and WebSock as much as possible, interpreting requests only to the extent needed to manage a connection safely. That is a design choice with consequences: Bandit keeps internal policy and HTTP-level configuration minimal. Application behavior is handled through Plug and WebSock, while Bandit also exposes server-specific options at startup. The data flow is straightforward: a client connects, Thousand Island accepts the socket, Bandit parses the request according to HTTP/1.x or HTTP/2 rules, then hands off to your Plug or WebSock handler. For WebSockets, Bandit supports the WebSock API, which Phoenix Channels and LiveView use. The architecture is modular, and the separation between Thousand Island and Bandit is clear: one handles transports, the other handles protocol. That separation is why Bandit can support both HTTP and HTTPS with the same code path, as you will see in the setup section.

Getting It Running: Commands and Config Keys

The README gives two concrete paths. For Phoenix, add the dependency to your mix.exs: {:bandit, "~> 1.8"}. Then add one line to your endpoint configuration in config/config.exs: adapter: Bandit.PhoenixAdapter. After that, Phoenix should log that it is using Bandit. For a Plug application, you start Bandit as a child in your supervision tree. The example is: {Bandit, plug: MyApp.MyPlug}. You can also start it directly with Bandit.start_link(plug: MyPlug), which starts an HTTP server on the default port 4000. The complete list of options is defined by the Bandit.options/0 type. The README mentions that if you have exotic configuration options in your Phoenix endpoint, you may need to update them to work with Bandit, and points to the Bandit.PhoenixAdapter documentation. That is a real caveat: the drop-in claim has limits. For HTTPS, the README says the most common stumbling block is key and certificate data, and it provides an example that includes scheme: :https, though the snippet is truncated. You will need to consult the docs for the full TLS options.

Correctness: h2spec and Autobahn Scores

Bandit emphasizes correctness. The README claims its HTTP/2 implementation scores 100% on the h2spec suite in strict mode, and its WebSocket implementation scores 100% on the Autobahn test suite. Both run in CI. These are concrete, verifiable claims, and they matter for a server that handles binary protocols. HTTP/2 is notoriously tricky, with frame ordering, flow control, and header compression. A 100% h2spec score in strict mode means the server passes every conformance test in that suite. Similarly, Autobahn is the standard WebSocket test suite, covering framing, close handshake, and edge cases. Bandit also supports per-message compression as defined in RFC 7692. The README mentions extensive unit tests, Credo, Dialyzer, and performance regression tests. That is a strong testing story. The trade-off is that conformance suites are not the same as real-world interoperability. A server can pass h2spec and still have bugs in rare interactions with clients. But these scores give you a baseline that many production servers do not have.

Performance Claims: Read Them with Care

The README makes specific performance claims: in ongoing automated tests, Bandit's HTTP/1.x engine is up to 4x faster than Cowboy depending on concurrency, and HTTP/2 is up to 1.5x faster. These are not benchmark results you can reproduce from the README alone. The claims are tied to a GitHub Actions workflow (manual_benchmark.yml), which you can inspect. The phrase 'up to' is important: the 4x figure is for a specific workload, and the README says it depends on the number of concurrent requests. For HTTP/2, the advantage is smaller. The README also says memory use is 'as-good-or-better' than Cowboy, but gives no numbers. As a technology editor, I advise you to treat these as directional, not definitive. Run your own benchmarks with your actual endpoint and traffic pattern. The published comparison does not establish which implementation details cause any measured difference; profile your own workload before drawing that conclusion.

Limitations and When It Is the Wrong Tool

Bandit is not a universal replacement for Cowboy. The README says any Phoenix or Plug app should work as a drop-in, but then immediately notes exceptions: errors exist, and you should file an issue. The Phoenix adapter has a documented list of configuration options that may not translate. If your endpoint uses exotic settings, you may need to change them. That is a real limitation. Also, Bandit delegates to Plug and WebSock, which means it does not implement features outside those interfaces. If you need HTTP/2 server push, which is deprecated anyway, or if you need a custom transport that Thousand Island does not support, Bandit is the wrong tool. Cowboy has a longer history and a broader ecosystem of extensions, and some libraries in the Elixir ecosystem may still assume Cowboy. The README does not mention any such incompatibilities, but the drop-in claim is qualified. For a simple Plug or Phoenix app, Bandit works. For a complex deployment with custom socket handling or non-standard HTTP features, you should test carefully.

Alternatives: Cowboy and the Erlang Heritage

The obvious alternative is Cowboy, the Erlang HTTP server that Bandit aims to replace. Cowboy is mature, battle-tested, and has been the default Phoenix server for years. Its approach is different: it is written in Erlang, uses its own connection management, and exposes a lower-level API that is not specifically tied to Plug. Bandit is written in Elixir and built on Thousand Island, which gives it a more uniform codebase if you are an Elixir developer. The performance claims in the README are relative to Cowboy, so Cowboy is the baseline. Cowboy also supports HTTP/2 and WebSockets, but its conformance testing is not as prominently documented. If you need the absolute longest track record, Cowboy is safer. If you want a server that is easier to read and modify, Bandit is the choice. Another alternative is to use the built-in Plug.Cowboy adapter, which is what you replace when you switch to Bandit. The difference is that Plug.Cowboy is a bridge, while Bandit is a native implementation. That is the core distinction: one is an adapter, the other is a native server.

Maintenance, Upgrade Cost, and License

Bandit is MIT licensed, which means you can use it in commercial projects without copyleft obligations. The project is not archived, and the default branch is main, but the README does not give a release cadence or a changelog. The dependency version in the Phoenix example is ~> 1.8, so you should expect breaking changes between major versions, but the exact upgrade path is not documented in the README. The project has a companion library, Thousand Island, which is also from the same author, so you are depending on two libraries. The upgrade cost is moderate: because Bandit aims to be a drop-in, you can switch back to Cowboy if needed, but the README warns about exotic configuration. There is no migration guide in the README beyond the Phoenix adapter note. The maintenance story is tied to the author (mtrudel) and the community around Phoenix, since Phoenix now defaults to Bandit. That gives it a stable base of users, but the README does not state a formal governance model. You should check the hex.pm page for the latest version and any deprecation notices before adopting.

Editorial conclusion

Adopt Bandit if you run a Phoenix or Plug app and want a server written entirely in Elixir with strong HTTP/2 and WebSocket conformance, and if you accept a smaller ecosystem than Cowboy's. Skip it if you need exotic endpoint configuration that the Phoenix adapter does not translate, or if you rely on Cowboy-specific features like advanced routing or custom transports. Before switching, verify your endpoint config against Bandit.PhoenixAdapter docs, run your own benchmarks for your concurrency profile, and check that all your dependencies that reach for Cowboy are compatible. Bandit is the default Phoenix server, so for new Phoenix 1.7.11+ apps it is the path of least resistance, but for existing apps with custom Cowboy hooks, the migration cost may not be worth it.

Frequently asked questions

What is Bandit in Elixir?

It is a pure Elixir HTTP server for applications built on Plug and WebSock, written entirely in Elixir and built on top of the author's separate socket-handling library. It serves HTTP/1.x, HTTP/2 and WebSocket clients over both HTTP and HTTPS, and has been the default HTTP server for Phoenix since framework release 1.7.11.

Is Bandit a drop-in replacement for Cowboy?

The project says any Phoenix or Plug application should work as a drop-in replacement, and that exceptions are bugs worth reporting. The caveat is that exotic endpoint configuration options may need adjusting, and WebSocket features require Phoenix 1.7 or later.

How does Bandit verify protocol correctness?

Its HTTP/2 implementation scores full marks on an external conformance suite in strict mode, and its WebSocket implementation scores full marks on a separate external WebSocket suite. Both suites run as part of the project's continuous integration, and each supported protocol is mapped in the status section to the specification it implements.

How does Bandit compare to Cowboy on performance?

The README reports the HTTP/1.x engine as up to four times faster than Cowboy depending on the number of concurrent requests, and up to one and a half times faster on HTTP/2, from ongoing automated benchmark tests it links. The stated reason is that Bandit was built from the ground up for Plug applications.

Does Bandit have any configuration options?

It aims to have minimal HTTP-level configuration, delegating as much as possible to Plug and WebSock and only interpreting requests far enough to manage a connection safely. Options are passed when starting the server, either as a child in your supervision tree or through a start function, and the complete list lives in a documented options type rather than in the README.

Official sources

  1. Official README
  2. Project repository
  3. README
  4. Releases
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/mtrudel-bandit.svg)](https://hysenlabs.com/projects/mtrudel-bandit)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/mtrudel-bandit.svg)](https://hysenlabs.com/projects/mtrudel-bandit)

Was this useful?