KAHIBARO
Discord Login Register

22.2. Uvicorn

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:

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

bash
pip install uvicorn

This gives you:

With extra performance dependencies

Uvicorn can optionally use faster implementations for some parts, especially event loops and HTTP parsing.

bash
pip install "uvicorn[standard]"

The [standard] extra tries to install common performance dependencies such as:

You can check that Uvicorn is available:

bash
uvicorn --version

Running 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:

python
# 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:

bash
uvicorn app:app --reload

Open http://127.0.0.1:8000 and you should see:

Hello from a raw ASGI app!

Explanation:

Using Uvicorn with FastAPI

Uvicorn is the standard server for FastAPI applications.

Example FastAPI app

Create main.py:

python
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
    return {"message": "Hello from FastAPI + Uvicorn"}

Run with Uvicorn:

bash
uvicorn main:app --reload

You will see logs such as:

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

Visit:

You can also set host and port:

bash
uvicorn main:app --reload --host 0.0.0.0 --port 8000

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.

OptionExampleDescription
--host--host 0.0.0.0Bind IP address to listen on
--port--port 8000TCP port to listen on
--reload--reloadAuto-reload on code changes (dev only)
--workers--workers 4Number of worker processes
--log-level--log-level infoLogging level (debug, info, warning…)
--proxy-headers--proxy-headersTrust headers from reverse proxy
--forwarded-allow-ips--forwarded-allow-ips="*"Which IPs to trust for proxy headers
--app-dir--app-dir srcAdd a directory to the import path
--env-file--env-file .envLoad environment variables from a file

Example: basic development server

bash
uvicorn main:app --reload --host 127.0.0.1 --port 8000

Example: multi-worker server (more production-like)

bash
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:

Example run.py:

python
# run.py
import uvicorn
if __name__ == "__main__":
    uvicorn.run(
        "main:app",
        host="0.0.0.0",
        port=8000,
        reload=True,
    )

Now run:

bash
python run.py

You get the same result as:

bash
uvicorn main:app --host 0.0.0.0 --port 8000 --reload

Uvicorn and ASGI

ASGI (Asynchronous Server Gateway Interface) is a standard interface between async Python web servers and applications.

Uvicorn:

Example of parts of an ASGI scope for HTTP:

python
{
    "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:

Development vs Production Usage

You will usually use Uvicorn differently in development and production.

Development pattern

Typical development command:

bash
uvicorn main:app --reload --host 127.0.0.1 --port 8000

Characteristics:

Production pattern (simplified)

Production setups often involve:

A very simple direct production-like command (without Gunicorn) could be:

bash
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

But in many real-world deployments, you will instead use something like:

bash
gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:8000

The 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:

dockerfile
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:

bash
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:

Example:

bash
uvicorn main:app --reload --log-level debug

This can help you see:

If you want colored logs during development, install the standard extras:

bash
pip install "uvicorn[standard]"

Then run normally:

bash
uvicorn main:app --reload

Handling 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:

To allow Uvicorn to trust and use these headers, enable:

bash
uvicorn main:app --proxy-headers --forwarded-allow-ips="*"

Or specify a list of IPs you trust:

bash
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

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

Comments

Please login to add a comment.

Don't have an account? Register now!