Library / SDK
emmett-framework/granian avatar
emmett-framework/granian

Granian: a Rust HTTP server for Python ASGI, RSGI and WSGI apps

A Rust HTTP server for Python applications

5,675 stars175 forksRustBSD-3-Clause

At a glance

What is it?
Granian replaces the Gunicorn plus uvicorn plus httptools stack with one Rust binary built on Hyper and Tokio. It is a strong fit for HTTP/2 and websocket-heavy ASGI services, and a poor fit for trio, gevent or pure-Python deployments.
Who is it for?
Adopt Granian if you serve ASGI or WSGI over HTTP/2, want websockets without assembling Gunicorn, uvicorn and httptools separately, and can accept that ASGI extensions beyond pathsend are still open in issue 93. Do not adopt it if your application runs on trio or gevent, if you need advanced Python-level debugging inside the server, or if a pure-Python dependency tree is a hard requirement.
Can I use it commercially?
Yes. BSD-3-Clause 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 16 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Granian replaces and who it is aimed at

The README states the rationale directly: Granian exists to avoid the usual Gunicorn plus uvicorn plus http-tools dependency composition on unix systems, and to provide a single package for several platforms. That is the pitch. Instead of layering a process manager, an ASGI server and a C HTTP parser, you install one distribution and get HTTP/1, HTTP/2, HTTPS, mTLS, websockets and static file serving from one Rust codebase built on Hyper and Tokio.

The audience is narrow and specific. The README lists the cases where Granian is a good choice: a modern single dependency for both ASGI and WSGI, the most performant way to serve a Python application under HTTP/2, good concurrency for websockets, and a preference for throughput over everything else. The same section lists the cases where it is not: pure Python solutions, advanced debugging features, applications that rely on trio or gevent, and ASGI extensions that are not yet implemented. Those exclusions come from the project itself, not from a competitor, which makes them worth taking at face value.

Interface support is broader than most Python servers. Granian speaks ASGI, RSGI (its own spec, documented in docs/spec/RSGI.md) and WSGI. A Django or Flask application can run under the same binary as a FastAPI or Starlette one, which is the practical argument for teams that maintain both.

How Granian serves a request: workers, runtime threads and blocking threads

Granian is a compiled extension module. The Cargo.toml declares a cdylib named _granian, and the Python package under granian/ is a thin CLI layer on top of it. The server itself is Rust: Hyper handles HTTP/1 and HTTP/2, Tokio provides the async runtime, and PyO3 bridges into the Python interpreter.

The process model is explicit in the CLI options. --workers sets the number of worker processes and defaults to 1. Within each worker, --runtime-threads sets the number of runtime threads (default 1) and --blocking-threads sets the number of blocking threads per worker. There is a separate --runtime-blocking-threads option for runtime I/O blocking threads, and --runtime-mode chooses between single-threaded and multi-threaded operation, defaulting to auto. This is a finer-grained concurrency model than the usual one-process-per-core arrangement: you can run one worker with several runtime threads, or several workers with one thread each, and the two are not equivalent.

Backpressure is handled per worker. --backpressure caps how many requests are processed concurrently and defaults to backlog divided by workers; --backlog caps the global connection backlog and defaults to 1024 with a minimum of 128. The --blocking-threads-idle-timeout option controls how long an idle blocking thread stays alive, defaulting to 30 seconds within a documented range of 5 to 600.

Event loop selection is delegated to --loop, which accepts auto, asyncio, rloop, uvloop or winloop. The default is auto. Note the platform split in pyproject.toml: uvloop and rloop are excluded on win32, and winloop is win32-only. On Windows the default loop is therefore not uvloop, and installing granian[uvloop] there does nothing useful.

One design point worth flagging: --task-impl accepts asyncio or rust and defaults to asyncio. The Rust task implementation exists but is not the default, so the async task scheduling most users get is the standard asyncio one. Anyone expecting the Rust runtime to also own task scheduling has to opt in.

Installing Granian and serving a first ASGI app

The README gives a single install command: pip install granian. Optional extras are declared in pyproject.toml as dotenv, pname, reload, rloop, uvloop and winloop, plus an all extra that bundles dotenv, pname and reload. Extras combine, and the README's example is granian[pname,uvloop].

A minimal ASGI application in main.py, copied from the README:

python
async def app(scope, receive, send):
    assert scope['type'] == 'http'

    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [
            [b'content-type', b'text/plain'],
        ],
    })
    await send({
        'type': 'http.response.body',
        'body': b'Hello, world!',
    })

Serve it by naming the interface and the module attribute:

bash
granian --interface asgi main:app

The server binds 127.0.0.1:8000 by default, so a request to that address should return the plain-text body. Every option has an environment variable equivalent, which matters for container deployments: GRANIAN_HOST, GRANIAN_PORT, GRANIAN_INTERFACE, GRANIAN_HTTP, GRANIAN_WORKERS and the rest. Setting GRANIAN_WORKERS is often easier than threading a flag through a process manager.

The WSGI path is the same shape with a different interface value:

bash
granian --interface wsgi main:app

The WSGI app itself is the ordinary two-argument callable returning a list of byte strings, as shown in the README. The RSGI interface is also available via --interface rsgi, which the CLI lists as the default when no interface is specified.

Where Granian is the wrong tool

The clearest limitation is stated by the project: applications relying on trio or gevent are not a good fit. Granian's concurrency is built on Tokio and the asyncio-compatible loop options, so a codebase whose async model is trio cannot simply be pointed at it. There is no trio loop value in --loop, and no documented gevent monkey-patching path.

ASGI extensions are the second gap. The README links issue 93 for extensions that are not yet implemented, and separately notes that the pathsend extension is supported. That means an application or framework that depends on another extension has to be checked before migration, and the README does not enumerate which extensions are missing. This is the kind of thing that bites during a framework upgrade, not during the initial smoke test.

Debugging is the third. The README lists advanced debugging features as a reason Granian will not be the ideal option, which is a fair description of a compiled server: the request path runs through Rust, and Python-level introspection into the server itself is limited compared to a pure-Python implementation.

HTTP/3 is not there yet either. The rationale says the goal is a single correct HTTP implementation supporting versions 1, 2 and eventually 3, and the --http option accepts only auto, 1 or 2. If you need HTTP/3 today, this is not the server for it.

Granian compared with uvicorn and Gunicorn

The obvious comparison is uvicorn, and the difference is architectural rather than a matter of flags. Uvicorn is a Python ASGI server that uses httptools (a C extension) for HTTP parsing and uvloop optionally for the event loop. Granian moves the entire HTTP implementation and the runtime into Rust, with PyO3 as the boundary. The README's rationale names this explicitly: a single correct HTTP implementation and avoiding the Gunicorn plus uvicorn plus http-tools composition.

The practical consequence is dependency count and process management. With Granian, worker management is a server option (--workers) rather than a separate Gunicorn process, and HTTP/2 support is part of the server instead of something you assemble. With uvicorn, HTTP/2 typically requires a different deployment shape. That is the real dividing line.

The trade-off runs the other way for extensibility and debugging. Uvicorn is Python, so its internals are inspectable and its extension surface is broad. Granian's README concedes the debugging point. RSGI is also a Granian-specific interface: adopting it ties your application to this server, whereas an ASGI application can move between uvicorn, Hypercorn and Granian. If portability matters more than throughput, ASGI is the safer interface choice even under Granian.

The project publishes its own comparison in benchmarks/vs.md and a benchmark directory in the repository. Those are the maintainers' numbers, produced from their own harness, and should be read that way.

Licence, maintenance and the cost of upgrading

Granian is BSD-3-Clause, declared in Cargo.toml and matching the BSD classifier in pyproject.toml. That is a permissive licence, and it is the same licence family used by many Python web servers, so it rarely creates friction in commercial deployments. The repository also lists a funding link in project.urls. Nothing here is legal advice; if licence compatibility matters to your organisation, read the LICENSE file at the repository root.

Maintenance signals are concrete. The repository is not archived, and the last push was on 2026-09-14. Releases are frequent: v2.8.1 on 2026-08-05, v2.8.2 on 2026-08-23 and v2.8.3 on 2026-09-14. The version in Cargo.toml matches the release tag, and the Python package version is dynamic, so the wheel version and the crate version move together.

Upgrade cost depends on which interface you choose. ASGI applications carry the least risk because the interface is standard and the server is replaceable. RSGI applications carry more, because the spec lives in this repository and moves with it. The Python requirement is 3.10 or newer, per requires-python, and the classifiers list CPython and PyPy plus a Free Threading beta marker. The runtime dependency list is short: click 8.1.0 or newer is the only mandatory dependency, with everything else behind extras.

Building from source is heavier. The Makefile's build-dev target runs uv sync --group all followed by uv run maturin develop --uv, which means a Rust toolchain and maturin. For most users the published wheel is the path; source builds are for contributors and for platforms without a wheel.

Editorial conclusion

Adopt Granian if you serve ASGI or WSGI over HTTP/2, want websockets without assembling Gunicorn, uvicorn and httptools separately, and can accept that ASGI extensions beyond pathsend are still open in issue 93. Do not adopt it if your application runs on trio or gevent, if you need advanced Python-level debugging inside the server, or if a pure-Python dependency tree is a hard requirement. Before committing, check that your framework's ASGI usage avoids the unimplemented extensions, confirm which loop extra you need on your platform (uvloop and rloop are excluded on win32, winloop is win32-only), and verify your Python version is 3.10 or newer.

Frequently asked questions

What is Granian?

Granian is a Rust HTTP server for Python applications, built on Hyper and Tokio and installed as a Python package. It serves ASGI, RSGI and WSGI applications and supports HTTP/1 and HTTP/2, HTTPS, mTLS, websockets and static files.

How does Granian compare with uvicorn in performance?

The project publishes its own comparison in benchmarks/vs.md and a benchmarks directory in the repository, and its stated goal is stable performance relative to existing alternatives. Those figures come from the maintainers' harness, so treat them as the project's own measurements rather than independent results.

Can Granian serve a FastAPI application?

FastAPI is an ASGI framework, and Granian supports the ASGI interface, so it can be served with the asgi interface value. The README's ASGI example uses a raw ASGI callable, and the CLI's --interface option accepts asgi among its values.

How does Granian differ from uWSGI?

The material describes Granian as a Rust server built on Hyper and Tokio with ASGI, RSGI and WSGI interfaces, and its rationale is to avoid the Gunicorn plus uvicorn plus http-tools composition. It does not document uWSGI, so a direct feature comparison cannot be made from the repository.

Official sources

  1. emmett-framework/granian on GitHub
  2. Issues
  3. License: BSD-3-Clause
  4. README
  5. 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/emmett-framework-granian.svg)](https://hysenlabs.com/projects/emmett-framework-granian)