Gunicorn vs Uvicorn vs Hypercorn: Which Server?
Gunicorn, Uvicorn and Hypercorn compared: what each one does, WSGI versus ASGI, how to combine them in production, and which to pick for your app.
Use Gunicorn for WSGI apps like Flask and Django, Uvicorn for ASGI apps like FastAPI, and add Hypercorn only if you need HTTP/2 or the trio async library. For ASGI in production you can run Uvicorn alone, or have Gunicorn manage Uvicorn workers.
WSGI and ASGI in one minute
Python web servers speak one of two interfaces to your app. WSGI is the older, synchronous one used by Flask and Django’s classic mode. ASGI is the async successor, which also handles WebSockets, and is used by FastAPI, Starlette and Django’s async mode. The Uvicorn explainer covers how the layers fit.
A server that speaks WSGI cannot run an ASGI app, and the reverse. That is the first thing to check.
At a glance
| Gunicorn | Uvicorn | Hypercorn | |
|---|---|---|---|
| Interface | WSGI; ASGI through a worker class | ASGI | ASGI |
| Process model | Pre-fork: an arbiter manages worker processes | One process, or --workers N using spawn | Workers set with -w; default 1 |
| Async support | Via gevent, or a Uvicorn worker for ASGI | Native | asyncio, uvloop or trio |
| Typical pairing | Flask, Django, or FastAPI with Uvicorn workers | FastAPI, Starlette | Quart, or any ASGI app that needs HTTP/2 or trio |
Gunicorn
Gunicorn uses a pre-fork worker model. An arbiter process manages a pool of workers and never touches client sockets; the workers handle requests. It restarts workers that crash and responds to signals: TTIN and TTOU add or remove workers, and HUP reloads the configuration.
Worker types from Gunicorn’s design documentation:
- sync (the default): one request per worker at a time, no keep-alive, meant to sit behind a buffering proxy.
- gthread: a thread pool per worker, with keep-alive.
- gevent: greenlet-based async, suited to high-concurrency I/O and WebSockets.
- tornado: for native Tornado applications.
- ASGI: run async frameworks through a Uvicorn worker class.
For the worker count, the documentation suggests (2 x CPU cores) + 1 as a starting point to adjust under load. It also warns that too many workers can reduce throughput.
gunicorn -w 4 myapp:app
Uvicorn
Uvicorn is a fast ASGI server and the default choice for FastAPI. For production, its deployment documentation describes two routes.
Built-in workers. --workers runs several processes, restarts any that die unexpectedly and kills stuck ones:
uvicorn main:app --workers 4
It uses spawn rather than pre-fork, so it also works on Windows. --workers and --reload cannot be combined. On non-Windows systems, SIGHUP restarts workers one at a time, and SIGTTIN and SIGTTOU add and remove workers.
Under Gunicorn. Run Gunicorn as the process manager with Uvicorn as the worker class:
gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
Uvicorn’s documentation notes that uvicorn.workers is deprecated and will be removed, and recommends the separate uvicorn-worker package (python -m pip install uvicorn-worker). It doesn’t state the replacement class path on that page, so check the package’s own documentation before you deploy. It also notes that Gunicorn doesn’t support every Uvicorn option, --limit-concurrency for example.
Hypercorn
Hypercorn is an ASGI server that supports HTTP/1, HTTP/2 and WebSockets (over HTTP/1 and HTTP/2). Its distinguishing feature is the choice of worker type: asyncio, uvloop or trio. It began as part of the Quart framework and was split out as a standalone server.
hypercorn main:app --bind 127.0.0.1:8000 --workers 2
Options include -b/--bind, -k/--worker-class and -w/--workers, plus --certfile and --keyfile for TLS and --quic-bind for a UDP/QUIC address. The uvloop and trio workers need extras, for example pip install hypercorn[uvloop]. Settings can also be loaded from a TOML file with hypercorn --config.
Behind a reverse proxy
Whichever you choose, put it behind a reverse proxy such as Nginx in production if you can. Uvicorn’s documentation lists the benefits: it can serve static files and buffer slow requests, and a UNIX domain socket between the proxy and Uvicorn is preferred.
The proxy must pass the client’s address and scheme in X-Forwarded-For and X-Forwarded-Proto. Tell Uvicorn which proxies to trust with --forwarded-allow-ips, and only trust proxies you control, since anyone can send these headers. Setting it to "*" is safe only if your proxies strip incoming forwarded headers.
Which should you pick?
- Flask or Django (WSGI): Gunicorn.
- FastAPI or Starlette, one container, simple setup: Uvicorn with
--workers, or a single process per container if your orchestrator scales containers for you. - FastAPI with Gunicorn already in your stack: Gunicorn managing Uvicorn workers.
- You need HTTP/2 or want to run on trio: Hypercorn.
- You are not sure: start with Uvicorn for ASGI and Gunicorn for WSGI. Both are the common defaults, and switching later is a one-line change to the start command.
For containers, see the Docker for Python with uv guide, and for framework choice, Django vs FastAPI vs Flask.
Frequently Asked Questions
What is the difference between Gunicorn and Uvicorn?
Gunicorn is a pre-fork process manager built for WSGI apps such as Flask and Django. Uvicorn is an ASGI server for async apps such as FastAPI and Starlette. Gunicorn can run Uvicorn as a worker class, which gives you Gunicorn’s process management with Uvicorn’s async serving.
Do I need Gunicorn if I use Uvicorn?
Not necessarily. Uvicorn has its own —workers option, for example uvicorn main:app —workers 4, which runs several processes and restarts any that die. Use Gunicorn in front when you already run it, or want its mature process management.
What is Hypercorn?
Hypercorn is an ASGI server that supports HTTP/1, HTTP/2 and WebSockets, and can run on asyncio, uvloop or trio. It started as part of the Quart framework and was split out as a standalone server.
How many Gunicorn workers should I run?
Gunicorn’s documentation gives (2 x CPU cores) + 1 as a starting point to adjust under load, and notes that 4 to 12 workers typically handle heavy traffic. Too many workers can reduce throughput.
Is uvicorn.workers.UvicornWorker deprecated?
Uvicorn’s deployment documentation says the uvicorn.workers module is deprecated and will be removed, and recommends the separate uvicorn-worker package. Check the current documentation for the replacement class before you deploy.
Should I put Nginx in front?
Uvicorn’s documentation calls a reverse proxy optional but recommended: it can serve static files and buffer slow requests. If you do, set the forwarded headers and trust only proxies you control.