Bandit: An Elixir-Native HTTP Server That Replaces Cowboy in Phoenix
Bandit is a pure Elixir HTTP server for Plug & WebSock applications.
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 8 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 September 14, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE 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. This means you configure your app through Plug and WebSock, not through server-specific settings. 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 performance edge likely comes from Bandit being built from the ground up for Plug, avoiding the overhead of translating between Erlang and Elixir abstractions. But that is an inference from the README's wording, not a measured fact.
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.
Community notes