22.2. Uvicorn
Table of Contents
What Uvicorn Is
Uvicorn is a lightweight, high-performance ASGI server written in Python. It is most commonly used to run async web frameworks such as FastAPI, Starlette, and modern Django (via ASGI).
You can think of Uvicorn as the piece that actually:
- Listens on a port, for example
http://127.0.0.1:8000 - Accepts HTTP connections from clients
- Translates them into ASGI calls to your Python application
- Sends back responses to the client
Your FastAPI or other ASGI app does not talk directly to the raw network. Uvicorn sits in between and handles the low-level networking work.
Key idea:
Uvicorn is an ASGI server. It runs ASGI applications (like FastAPI apps) by handling network I/O and calling your app according to the ASGI specification.
Compared to older WSGI servers (like Gunicorn alone), Uvicorn is designed from the start for asynchronous Python code and concurrent connections.
Installing Uvicorn
You usually install Uvicorn from PyPI using pip. It is best practice to do this inside a virtual environment for each project.
Basic installation
pip install uvicornThis gives you:
- The
uvicorncommand in your environment - The Python package
uvicornfor programmatic use
With extra performance dependencies
Uvicorn can optionally use faster implementations for some parts, especially event loops and HTTP parsing.
pip install "uvicorn[standard]"
The [standard] extra tries to install common performance dependencies such as:
uvloopfor a faster event loop (on Unix-like systems)httptoolsfor faster HTTP parsing- Other useful extras like
python-dotenv
You can check that Uvicorn is available:
uvicorn --versionRunning a Simple ASGI App with Uvicorn
Before using Uvicorn with a full framework like FastAPI, it helps to see how it runs a minimal ASGI app.
A minimal ASGI application
Create app.py:
# app.py
async def app(scope, receive, send):
# Every ASGI app receives:
# - scope: connection information
# - receive: coroutine to get events
# - send: coroutine to send events
assert scope["type"] == "http"
# Send HTTP response start
await send({
"type": "http.response.start",
"status": 200,
"headers": [
[b"content-type", b"text/plain; charset=utf-8"],
],
})
# Send HTTP response body
await send({
"type": "http.response.body",
"body": b"Hello from a raw ASGI app!",
})Now run it with Uvicorn:
uvicorn app:app --reload
Open http://127.0.0.1:8000 and you should see:
Hello from a raw ASGI app!Explanation:
app:appmeans:app(left of:) is the Python module name, hereapp.pyapp(right of:) is the ASGI app object inside that module--reloadenables automatic reload in development when code changes
Using Uvicorn with FastAPI
Uvicorn is the standard server for FastAPI applications.
Example FastAPI app
Create main.py:
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "Hello from FastAPI + Uvicorn"}Run with Uvicorn:
uvicorn main:app --reloadYou will see logs such as:
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)Visit:
http://127.0.0.1:8000/for the root endpointhttp://127.0.0.1:8000/docsfor automatic Swagger UI docs
You can also set host and port:
uvicorn main:app --reload --host 0.0.0.0 --port 8000--host 0.0.0.0tells Uvicorn to listen on all network interfaces, which is useful inside Docker containers or when exposing your app on a local network.
Important rule:
In development, use --reload.
In production, do not use --reload. It is slower and not intended for production.
Common Uvicorn Command-Line Options
Here are some of the most common flags you will use when starting Uvicorn.
| Option | Example | Description |
|---|---|---|
--host | --host 0.0.0.0 | Bind IP address to listen on |
--port | --port 8000 | TCP port to listen on |
--reload | --reload | Auto-reload on code changes (dev only) |
--workers | --workers 4 | Number of worker processes |
--log-level | --log-level info | Logging level (debug, info, warning…) |
--proxy-headers | --proxy-headers | Trust headers from reverse proxy |
--forwarded-allow-ips | --forwarded-allow-ips="*" | Which IPs to trust for proxy headers |
--app-dir | --app-dir src | Add a directory to the import path |
--env-file | --env-file .env | Load environment variables from a file |
Example: basic development server
uvicorn main:app --reload --host 127.0.0.1 --port 8000Example: multi-worker server (more production-like)
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
Note that when using Uvicorn with Gunicorn in production, Gunicorn usually manages the workers, not Uvicorn. That integration is often written as gunicorn -k uvicorn.workers.UvicornWorker .... Details belong to the Application Servers chapter, so here we only mention the idea.
Programmatic Usage
You do not have to call Uvicorn from the command line. You can also start it from Python code. This is sometimes useful for:
- Custom scripts
- Special testing setups
- Embedding Uvicorn inside another process
Example run.py:
# run.py
import uvicorn
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True,
)Now run:
python run.pyYou get the same result as:
uvicorn main:app --host 0.0.0.0 --port 8000 --reloadUvicorn and ASGI
ASGI (Asynchronous Server Gateway Interface) is a standard interface between async Python web servers and applications.
Uvicorn:
- Implements the server side of ASGI
- Accepts HTTP connections
- For each request, creates a
scopedictionary with connection info
Example of parts of an ASGI scope for HTTP:
{
"type": "http",
"http_version": "1.1",
"method": "GET",
"path": "/",
"headers": [
[b"host", b"127.0.0.1:8000"],
[b"user-agent", b"..."],
],
# other fields...
}Your ASGI app receives this scope and uses it to decide how to respond. Frameworks like FastAPI hide this low-level detail and give you higher-level route handlers instead.
Because Uvicorn is ASGI-based, it can also handle:
- WebSockets
- Lifespan events
- Potentially other protocols defined in ASGI
Development vs Production Usage
You will usually use Uvicorn differently in development and production.
Development pattern
Typical development command:
uvicorn main:app --reload --host 127.0.0.1 --port 8000Characteristics:
--reloadwatches your code files and restarts when you modify them- One or few workers
- Accessible only locally or in your dev environment
Production pattern (simplified)
Production setups often involve:
- Uvicorn as the application server
- A reverse proxy such as Nginx or Traefik in front
- Possibly Gunicorn managing multiple Uvicorn workers
A very simple direct production-like command (without Gunicorn) could be:
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4But in many real-world deployments, you will instead use something like:
gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:8000The details of these integration patterns belong to other chapters. What you need here is the idea that Uvicorn is the ASGI engine that actually runs your async app.
Uvicorn with Docker
Uvicorn is very commonly used inside Docker containers.
A minimal Dockerfile example:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]Then:
docker build -t my-fastapi-app .
docker run -p 8000:8000 my-fastapi-app
Now the app is available on your host at http://127.0.0.1:8000.
In more advanced Docker setups, you might combine Uvicorn with Gunicorn, but the core idea remains: inside the container, something uses Uvicorn to run your ASGI app.
Logging and Debugging with Uvicorn
Uvicorn prints logs to standard output by default. You can control verbosity with --log-level.
Common levels:
criticalerrorwarninginfodebugtrace(very detailed)
Example:
uvicorn main:app --reload --log-level debugThis can help you see:
- Incoming requests
- Errors or stack traces
- Startup and shutdown events
If you want colored logs during development, install the standard extras:
pip install "uvicorn[standard]"Then run normally:
uvicorn main:app --reloadHandling Proxy Headers
In many production setups, your Uvicorn server runs behind a reverse proxy like Nginx or Traefik. The proxy often forwards information to Uvicorn using headers such as:
X-Forwarded-ForX-Forwarded-ProtoX-Forwarded-Host
To allow Uvicorn to trust and use these headers, enable:
uvicorn main:app --proxy-headers --forwarded-allow-ips="*"Or specify a list of IPs you trust:
uvicorn main:app --proxy-headers --forwarded-allow-ips="127.0.0.1"
Security rule:
Only enable --proxy-headers and trust X-Forwarded-* headers when requests come only through a trusted reverse proxy. Do not trust these headers directly from the public internet.
Summary
- Uvicorn is a high-performance ASGI server for Python, ideal for running FastAPI and other modern async frameworks.
- Install it with
pip install uvicornorpip install "uvicorn[standard]". - You run your app with commands like
uvicorn main:app --reload --host 0.0.0.0 --port 8000. - Use
--reloadduring development, and avoid it in production. - Uvicorn exposes various options for host, port, workers, logging, and proxy headers.
- It fits naturally into Docker-based setups and production stacks with reverse proxies and, sometimes, Gunicorn.
Understanding Uvicorn gives you a clear picture of how your Python web code connects to the real network and serves actual HTTP requests.
Views: 7
KAHIBARO