# Ranch is an Erlang socket acceptor pool that can be swapped out without dropping connections

> Ranch is an ISC licensed Erlang library for accepting TCP connections, organised around a pool of acceptors rather than a single loop, with per-listener connection limits and the ability to upgrade the acceptor pool while open sockets stay up. It is built with erlang.mk, tested with common_test, eunit, stampede and Concuerror, and its documentation is generated from AsciiDoc.

**ninenines/ranch** — Socket acceptor pool for TCP protocols.

- Repository: https://github.com/ninenines/ranch
- Stars: 1,244 · Forks: 337
- Language: Erlang
- License: ISC
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/ninenines-ranch

## The pool is the interface, not the accept loop

Ranch is a socket acceptor pool for TCP protocols, and the goals section is unusually explicit about what that means. Ranch aims to provide everything you need to accept TCP connections with a small code base and low latency, while being easy to use directly as an application or to embed into your own.

That last clause is the design position. Ranch is a library, not a framework, and the goals section says it is meant to be embedded, which is the same argument a web server makes about sitting on top of a transport layer rather than reimplementing it.

The pool structure is the mechanism. Listeners accept and manage connections on one port and include facilities to limit the number of concurrent connections, and connections are sorted into pools, each pool having a different configurable limit. So the concurrency control is not a single global setting, it is a per-pool one, and a listener can have several pools with different limits attached to it.

The consequences of a pool design show up in two places. The first is operational: a limit is a policy you can express per pool rather than per server, so you can allow a large number of cheap connections and a small number of expensive ones. The second is that connections are managed objects rather than raw sockets, which is what makes the upgrade feature possible, because a socket that has been handed to a pool can outlive the acceptor that created it.

## Transport and protocol are chosen per listener

Ranch provides a modular design, letting you choose which transport and protocol are going to be used for a particular listener.

That is the extensibility story in one sentence, and it separates the two things that are often confused. A transport is how bytes move, and a protocol is what the bytes mean. Ranch lets you decide, per listener, which of each you are speaking, so you can add a new protocol implementation without touching the acceptance machinery, and you can change how a listener accepts without changing the protocol above it.

The protocol implementations are separate applications in the Erlang ecosystem rather than part of this repository, and the Makefile reflects the modularity in a practical way. The application is registered as ranch_server, and the local dependency list is just ssl, with PLT_APPS covering crypto, public_key and tools, which are the applications the TLS path needs.

For an adopter, the practical question is which combination you actually need. A TCP listener speaking a line protocol is the simple case. A listener speaking TLS is the common one, and it is the reason ssl is the local dependency. Anything more specific, a WebSocket framing or a binary protocol, is where the modular design pays for itself, because those are separate modules that plug into the acceptor rather than rewrites of it.

The two example directories in the repository show the shape of the smallest possible usage. examples/tcp_echo/ is the canonical minimal case, and examples/tcp_reverse/ is the other kind of thing people build, a listener that connects out to a backend and shuttles bytes between the two.

## The pool can be upgraded while sockets stay open

The single feature that distinguishes Ranch from a hand-written accept loop is stated in one line of the goals section: Ranch also allows you to upgrade the acceptor pool without having to close any of the currently opened sockets.

Think about what that means in production. A conventional acceptor owns its sockets, so replacing the acceptor means closing the connections, which means interrupting the clients. With a pool, the acceptors are the thing you replace, and the connections live in the pool independently of the acceptor that created them. So you can roll out a new acceptor configuration, or a new acceptor implementation, and the live connections continue.

This is the kind of property that only shows up in the design and not in the documentation of most servers, and it is the reason to embed Ranch rather than write your own accept loop. A hand-written gen_tcp acceptor has a single owner for the socket, and replacing that owner is a disruptive operation by construction.

The requirement that makes it work is the separation of concerns already described. Connections are managed objects in pools, the pools are bounded, and the acceptor is one more participant that can be stopped and replaced without invalidating the objects it created.

The cost of the design is a little more indirection at the start. Nothing in the goals section promises a one-line setup, and the examples directory is the place to see how much ceremony a listener actually needs.

## The build is erlang.mk and the documentation is AsciiDoc

The build is deliberately conventional for the Erlang ecosystem. The Makefile sets the project metadata and then includes erlang.mk, which is a make-based build system for Erlang projects.

```make
PROJECT = ranch
PROJECT_DESCRIPTION = Socket acceptor pool for TCP protocols.
PROJECT_VERSION = 2.3.0
PROJECT_REGISTERED = ranch_server
```

The four lines are worth reading, because the third one tells you the version the build is producing. It is 2.3.0, and the online documentation links in the README point at the matching 2.3 user guide and function reference under ninenines.eu/docs/en/ranch/2.3.

The documentation is generated rather than written twice. While still online, run make docs, and the user guide is available in doc/ in PDF and HTML formats, with the function reference as man pages in doc/man3/ and doc/man7/. The full documentation in AsciiDoc lives in doc/src/, and examples are in examples/. To install the man pages on your system, run make install-docs.

```bash
make docs
make install-docs
```

So the documentation pipeline needs AsciiDoc tooling, which the Makefile lists as DOC_DEPS with asciideck, and the offline PDF and HTML output means the guide is usable on a machine with no network access, which is the point of generating it into the tree.

For a library whose audience includes people writing a listener for the first time, having the manual available as man pages on the system is a small but real convenience, and the man3 and man7 split suggests the documentation is organised around the two kinds of thing a user touches.

## The test strategy goes past unit tests into concurrency checking

The Makefile shows a test setup that takes concurrency seriously, which is appropriate for a library whose entire job is managing many connections at once.

The baseline is common_test, with the options adding the test directory to the path and a ct_hooks option pointing at ranch_ct_hook. Eunit is used for the unit-level tests, with the test compilation options enabling the eunit_autoexport parse transform, and the compile options for the library itself turn on +warn_missing_spec and +warn_untyped_record, which makes the compiler warn about missing specs and untyped records.

The interesting part is stampede. It is a dependency pulled in at version 0.6.0, described in the Makefile as a havoc test suite, and it is excluded by default. The rule is that if FULL is not defined, the stampede suite is filtered out of the test suites, which means the expensive, randomised test does not run on every build and has to be asked for.

On top of that there is Concuerror, with two named test modules, ranch_concuerror for start_stop and for info, and the options for it are present but commented out. Concuerror is a tool that systematically explores interleavings of concurrent processes looking for errors, and running it against the start and stop paths of a socket pool is exactly where a race would hide.

Dialyzer is also set up but commented out, with a note that it would analyse the tests as well as the source. The CI configuration is explicit too, with the auto CI matrix set to OTP-LATEST-27+ on both Linux and Windows, and a ci.erlang.mk dependency providing the CI glue.

## ISC licensed, published as a Hex package, with no GitHub releases

The licence is ISC, and the Makefile carries the same value in the Hex metadata block, where licenses is set to ISC and the links section points at the 2.3 user guide, the function reference, the GitHub repository and the sponsor page. The ISC licence is short and permissive, and for a library that is embedded into other people's applications that is a good fit, with the usual condition of retaining the copyright notice and disclaimer. This is a licensing fact rather than legal advice.

The project is published to Hex, and the Makefile contains the extra metadata block that Hex uses for the package description, with the user guide and function reference as the primary links. The README also points at a Discord server, the GitHub issues tracker, and a sponsor link.

There are no GitHub releases. The version lives in the Makefile as 2.3.0, and the documentation URLs are versioned to match, which tells you the project versions the documentation with the code. The repository is not archived, the default branch is master, and the last push was on 2026-10-15, so the work is current.

Two final details about the packaging. The erlang.mk file sits at the root of the repository, which is the convention for projects that vendor their build system rather than requiring a global install, and the top-level entries include ebin/ for compiled beams, src/, test/ and doc/ alongside the Makefile and the README.asciidoc source of the readme itself.

## Conclusion

Ranch fits an Erlang or Elixir service that needs to accept many TCP connections with predictable limits and supervision, especially one that must survive a configuration change without dropping live sockets. It does not fit a project that needs a protocol implementation rather than a transport, since Ranch deliberately stops at accepting and managing connections. Before adopting it, read the 2.3 user guide for the transport and protocol choices, decide which listener limits you need, and note that the release is 2.3.0 in the build file while the repository publishes no GitHub releases.

## FAQ

### What does Ranch do in Erlang?

Ranch is a socket acceptor pool for TCP protocols, aiming to provide everything needed to accept TCP connections with a small code base and low latency, easy to use directly as an application or to embed into your own.

### How do I limit the number of concurrent connections with Ranch?

Listeners accept and manage connections on one port and include facilities to limit the number of concurrent connections. Connections are sorted into pools, and each pool has its own configurable limit, so the caps are per pool rather than per server.

### Can Ranch be reconfigured without dropping open connections?

Yes. The README states that Ranch allows you to upgrade the acceptor pool without having to close any of the currently opened sockets, which is the main reason to use a pool of acceptors rather than a hand-written accept loop.

### How do I build the Ranch documentation locally?

While still online, run make docs, which produces the user guide in PDF and HTML under doc/ and the function reference man pages under doc/man3/ and doc/man7/. The AsciiDoc sources are in doc/src/, and make install-docs installs the man pages on your system.

### How is Ranch tested beyond ordinary unit tests?

The Makefile runs common_test with a ranch_ct_hook and eunit for the unit level, adds stampede as a havoc test suite that is excluded unless FULL is defined, and defines two Concuerror test modules, ranch_concuerror for start_stop and for info, with the Concuerror options present but commented out. CI runs against OTP-LATEST-27+ on Linux and Windows.

## Sources

- [Issues](https://github.com/ninenines/ranch/issues)
- [License: ISC](https://github.com/ninenines/ranch/blob/master/LICENSE)
- [ninenines/ranch on GitHub](https://github.com/ninenines/ranch)
- [README](https://github.com/ninenines/ranch/blob/master/README.md)

---

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