uvicorn.run()

Master programmatic ASGI server control with Python's most powerful web server

Get Started

What is uvicorn.run()?

uvicorn.run() is the programmatic interface to Uvicorn, Python's lightning-fast ASGI web server. Unlike the command-line interface, it allows you to start and configure your server directly from Python code, giving you complete control over server lifecycle and configuration.

Perfect for production deployments, testing environments, and applications that need dynamic server configuration, uvicorn.run() is the go-to choice for modern Python web applications built with FastAPI, Starlette, and other ASGI frameworks.

Requirements: Python 3.10+ (as of uvicorn 0.40.0). Python 3.14 is supported since uvicorn 0.38.0. For optimal performance, install with pip install uvicorn[standard] which includes uvloop and httptools. The latest version is 0.41.0 (February 2026).

Quick Start

Get up and running with uvicorn.run() in seconds:

import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"message": "Hello, World!"}

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

That's it! Your ASGI application is now running with automatic reloading enabled for development.

Parameters Reference

Configure every aspect of your server with these comprehensive parameters:

Application

app

str | ASGI App

Your ASGI application. Can be a string in "module:attribute" format or the application object directly.

factory

bool

Treat app as an application factory (a callable that returns an ASGI app). Default: False

app_dir

str

Directory to add to sys.path when looking for the application module. Default: current working directory

Socket Binding

host

str

Bind socket to this host. Use "0.0.0.0" to make the server available externally. Default: "127.0.0.1"

port

int

Bind socket to this port. Use 0 to bind to an available port. Default: 8000

uds

str

Bind to a UNIX domain socket instead of TCP. Useful for reverse proxy setups.

fd

int

Bind to socket from this file descriptor. Useful for socket activation.

Development

reload

bool

Enable auto-reload for development. Watches for file changes and restarts the server. Default: False

reload_dirs

list[str]

Directories to watch for file changes when reload is enabled.

reload_delay

float

Delay in seconds between detecting a change and reloading. Default: 0.25

reload_includes

list[str]

Glob patterns for files to include in reload watching. Requires watchfiles.

reload_excludes

list[str]

Glob patterns for files to exclude from reload watching. Requires watchfiles.

Production

workers

int

Number of worker processes. Reads from $WEB_CONCURRENCY env var if set. Cannot be used with reload. Default: 1

env_file

str

Path to environment configuration file to load.

timeout_worker_healthcheck

int

Timeout in seconds for worker health check. Default: 5

Logging

log_level

str

Logging level: "critical", "error", "warning", "info", "debug", "trace". Default: "info"

log_config

str | dict

Logging configuration file path (.json or .yaml) or a dictionary config.

access_log

bool

Enable or disable access logging. Default: True

use_colors

bool

Enable or disable colorized log formatting. Default: auto-detected

Protocol Implementation

loop

str

Event loop implementation: "auto", "asyncio", "uvloop". Default: "auto"

http

str

HTTP protocol implementation: "auto", "h11", "httptools". Default: "auto"

ws

str

WebSocket protocol implementation: "auto", "none", "websockets", "websockets-sansio", "wsproto". Default: "auto"

lifespan

str

Lifespan protocol implementation: "auto", "on", "off". Default: "auto"

interface

str

Application interface type: "auto", "asgi3", "asgi2", "wsgi". Default: "auto"

h11_max_incomplete_event_size

int

Maximum size of incomplete HTTP events buffer (h11 only). Default: 16384

WebSocket Settings

ws_max_size

int

Maximum WebSocket message size in bytes. Default: 16777216 (16MB)

ws_max_queue

int

Maximum WebSocket receive queue length. Default: 32

ws_ping_interval

float

WebSocket ping interval in seconds. Default: 20.0

ws_ping_timeout

float

WebSocket ping timeout in seconds. Default: 20.0

ws_per_message_deflate

bool

Enable WebSocket per-message compression. Default: True

HTTP Settings

root_path

str

Set the ASGI root_path for applications mounted at a subpath. Default: ""

proxy_headers

bool

Enable X-Forwarded-Proto, X-Forwarded-For headers. Default: True

forwarded_allow_ips

str | list

Comma-separated list of trusted proxy IPs. Default: "127.0.0.1"

server_header

bool

Include "Server" header in responses. Default: True

date_header

bool

Include "Date" header in responses. Default: True

headers

list[tuple]

Custom HTTP headers to add to all responses.

HTTPS / SSL

ssl_keyfile

str

Path to SSL key file for HTTPS support.

ssl_certfile

str

Path to SSL certificate file for HTTPS support.

ssl_keyfile_password

str

Password to decrypt the SSL key file if encrypted.

ssl_version

int

SSL protocol version to use. Default: ssl.PROTOCOL_TLS_SERVER

ssl_cert_reqs

int

Whether client certificates are required. Default: ssl.CERT_NONE

ssl_ca_certs

str

Path to CA certificates file for client certificate verification.

ssl_ciphers

str

SSL ciphers to use. Default: "TLSv1"

Resource Limits

limit_concurrency

int

Maximum number of concurrent connections to allow before rejecting new connections.

limit_max_requests

int

Maximum number of requests per worker before terminating and respawning.

limit_max_requests_jitter

int

Jitter to add to limit_max_requests to stagger worker restarts and avoid all workers restarting simultaneously. Default: 0

backlog

int

Maximum number of pending connections in the connection backlog. Default: 2048

Timeouts

timeout_keep_alive

int

Timeout in seconds for keep-alive connections. Default: 5

timeout_graceful_shutdown

int

Timeout in seconds for graceful shutdown. After this time, connections are forcefully closed.

Real-World Examples

Production Configuration

import uvicorn
import os

if __name__ == "__main__":
    uvicorn.run(
        "app.main:app",
        host="0.0.0.0",
        port=int(os.getenv("PORT", 8000)),
        workers=int(os.getenv("WEB_CONCURRENCY", 4)),
        log_level="warning",
        access_log=False,
        proxy_headers=True,
        forwarded_allow_ips=os.getenv("TRUSTED_PROXIES", "127.0.0.1"),
        limit_max_requests=10000,  # Recycle workers to prevent memory leaks
        limit_max_requests_jitter=1000,  # Stagger worker restarts
        timeout_graceful_shutdown=30,
        ssl_keyfile="./ssl/key.pem",
        ssl_certfile="./ssl/cert.pem"
    )

Development with Auto-Reload

import uvicorn

if __name__ == "__main__":
    uvicorn.run(
        "app.main:app",
        host="127.0.0.1",
        port=8000,
        reload=True,
        reload_dirs=["./app"],
        reload_includes=["*.py", "*.html"],
        log_level="debug"
    )

Advanced Configuration with Config Object

import uvicorn
from uvicorn.config import Config
from uvicorn.server import Server

async def run_server():
    config = Config(
        "app.main:app",
        host="0.0.0.0",
        port=8000,
        log_level="info",
        access_log=True,
        use_colors=True
    )
    server = Server(config)
    await server.serve()

if __name__ == "__main__":
    import asyncio
    asyncio.run(run_server())

Factory Mode (Application Factory)

import uvicorn

# In app/main.py:
# def create_app():
#     app = FastAPI()
#     # Configure app with dependencies
#     return app

if __name__ == "__main__":
    uvicorn.run(
        "app.main:create_app",
        factory=True,  # Treat as factory function
        host="0.0.0.0",
        port=8000,
        reload=True
    )

UNIX Domain Socket (for Reverse Proxy)

import uvicorn

if __name__ == "__main__":
    uvicorn.run(
        "app.main:app",
        uds="/tmp/uvicorn.sock",  # UNIX socket instead of TCP
        workers=4,
        log_level="warning",
        proxy_headers=True,
        forwarded_allow_ips="*"  # Trust all (configure nginx properly)
    )

Testing Environment

import uvicorn
import threading
import time

def start_test_server():
    uvicorn.run(
        "tests.app:app",
        host="127.0.0.1",
        port=8001,
        log_level="critical",
        access_log=False
    )

# Start server in background for testing
server_thread = threading.Thread(target=start_test_server, daemon=True)
server_thread.start()
time.sleep(1)  # Wait for server to start

# Run your tests here

Best Practices

Performance & Security

  • Install with pip install uvicorn[standard] for uvloop and httptools performance boost
  • Use multiple workers in production for CPU-bound tasks (or use Gunicorn as process manager)
  • Disable access logs in production for better performance (access_log=False)
  • Use environment variables for configuration via env_file parameter
  • Enable SSL/TLS for production deployments with proper certificates
  • Set appropriate log levels for different environments ("warning" for production)
  • Use reload only in development environments (never in production)
  • Configure forwarded_allow_ips carefully when behind a proxy - only trust known proxies
  • Set limit_max_requests to recycle workers and prevent memory leaks, and use limit_max_requests_jitter to stagger restarts
  • Configure timeout_graceful_shutdown for clean shutdown handling

Development Tips

  • Use reload_dirs to watch specific directories for changes
  • Set debug log level during development (log_level="debug")
  • Use different ports for different services
  • Keep development and production configs separate
  • Use reload_includes/reload_excludes for fine-grained reload control (requires watchfiles)
  • Use factory mode (factory=True) for apps that need fresh instances on reload

Deployment Options

  • Use uvicorn's built-in workers option for simple multi-process deployment
  • For production, consider using Gunicorn with the uvicorn-worker package: gunicorn -k uvicorn_worker.UvicornWorker
  • Place behind a reverse proxy (nginx) for SSL termination, load balancing, and static files
  • Use UNIX domain sockets (uds) for reverse proxy communication to avoid TCP overhead

Deprecation Notices

Be aware of these deprecations when upgrading uvicorn:

uvicorn.workers module deprecated

The uvicorn.workers module is deprecated and will be removed in a future release. If you're using Gunicorn with uvicorn workers, migrate to the separate uvicorn-worker package:

pip install uvicorn-worker

Then use: gunicorn -k uvicorn_worker.UvicornWorker

Python 3.9 no longer supported

As of uvicorn 0.40.0 (December 2025), Python 3.9 support has been dropped. Upgrade to Python 3.10 or later.

ServerState deprecated from main module

As of uvicorn 0.34.1 (April 2025), importing ServerState from the main uvicorn module is deprecated. Import it from uvicorn.server instead.

Common Patterns

Environment-Based Configuration

import uvicorn
import os

def get_config():
    if os.getenv("ENVIRONMENT") == "production":
        return {
            "host": "0.0.0.0",
            "port": int(os.getenv("PORT", 8000)),
            "workers": int(os.getenv("WORKERS", 4)),
            "log_level": "warning",
            "access_log": False
        }
    else:
        return {
            "host": "127.0.0.1",
            "port": 8000,
            "reload": True,
            "log_level": "debug"
        }

if __name__ == "__main__":
    config = get_config()
    uvicorn.run("app.main:app", **config)

Graceful Shutdown

import uvicorn
import signal
import asyncio

class GracefulServer:
    def __init__(self):
        self.server = None
        self.should_exit = False
        
    async def run(self):
        config = uvicorn.Config("app.main:app", host="0.0.0.0", port=8000)
        self.server = uvicorn.Server(config)
        
        # Setup signal handlers
        for sig in (signal.SIGTERM, signal.SIGINT):
            signal.signal(sig, self.signal_handler)
            
        await self.server.serve()
    
    def signal_handler(self, signum, frame):
        print(f"Received signal {signum}")
        self.should_exit = True
        if self.server:
            self.server.should_exit = True

if __name__ == "__main__":
    server = GracefulServer()
    asyncio.run(server.run())