FastAPI Uvicorn Configuration
When deploying FastAPI applications to production, proper configuration of the Uvicorn server is essential for performance, reliability, and security. In this guide, we'll explore how to configure Uvicorn to get the most out of your FastAPI applications.
What is Uvicorn?
Uvicorn is an ASGI (Asynchronous Server Gateway Interface) server implementation that serves as the recommended server for running FastAPI applications. It's lightning-fast, built on uvloop and httptools, and designed specifically for asynchronous Python applications.
Basic Uvicorn Configuration
Let's start with a simple FastAPI application and see how to launch it with Uvicorn:
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return {"message": "Hello World"}
To run this application with Uvicorn, you can use either:
- Command line:
uvicorn main:app --reload
- Programmatically in Python:
# start_server.py
import uvicorn
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
Core Configuration Options
Uvicorn provides many configuration options that can be set either via command-line arguments or programmatically:
Host and Port
uvicorn main:app --host 0.0.0.0 --port 8000
Programmatically:
uvicorn.run("main:app", host="0.0.0.0", port=8000)
host="0.0.0.0"
: Makes the server available externallyport=8000
: Sets the port number (default is 8000)
Reload Mode
Enable auto-reload when files change (great for development):
uvicorn main:app --reload
Programmatically:
uvicorn.run("main:app", reload=True)
You can also specify which directories to watch:
uvicorn main:app --reload --reload-dir=app --reload-dir=utils
uvicorn.run("main:app", reload=True, reload_dirs=["app", "utils"])
Production Configuration
For production environments, you'll want to optimize performance and ensure reliability:
Workers
In production, you should run multiple worker processes to utilize all CPU cores:
uvicorn main:app --workers 4
A good rule of thumb is to use 2 * number_of_cores + 1
workers.
To use multiple workers directly with Uvicorn, you'll need to use Gunicorn as a process manager:
gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
Programmatically with Gunicorn:
from gunicorn.app.base import BaseApplication
class StandaloneApplication(BaseApplication):
def __init__(self, app, options=None):
self.options = options or {}
self.application = app
super().__init__()
def load_config(self):
for key, value in self.options.items():
if key in self.cfg.settings and value is not None:
self.cfg.set(key.lower(), value)
def load(self):
return self.application
if __name__ == "__main__":
options = {
"bind": "0.0.0.0:8000",
"workers": 4,
"worker_class": "uvicorn.workers.UvicornWorker",
}
from main import app
StandaloneApplication(app, options).run()
Concurrency and Performance Tuning
Uvicorn offers several options to fine-tune performance:
# Limit the number of concurrent connections
uvicorn main:app --limit-concurrency 1000
# Limit the maximum number of requests per worker
uvicorn main:app --limit-max-requests 10000
Programmatically:
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
limit_concurrency=1000,
limit_max_requests=10000
)
Timeout Configuration
Set timeouts to protect your application from slow clients:
# Keep-alive timeout (seconds)
uvicorn main:app --timeout-keep-alive 5
Programmatically:
uvicorn.run("main:app", timeout_keep_alive=5)
HTTPS Configuration
For secure HTTPS connections, provide SSL certificate files:
uvicorn main:app --ssl-keyfile key.pem --ssl-certfile cert.pem
Programmatically:
uvicorn.run(
"main:app",
host="0.0.0.0",
port=443,
ssl_keyfile="key.pem",
ssl_certfile="cert.pem"
)
Logging Configuration
Configure logging to help with debugging and monitoring:
# Set log level
uvicorn main:app --log-level debug
# Enable access log
uvicorn main:app --access-log
Programmatically:
uvicorn.run("main:app", log_level="debug", access_log=True)
You can also configure a custom logging configuration:
uvicorn main:app --log-config logging.conf
uvicorn.run("main:app", log_config="logging.conf")
Full Production Example
Let's create a comprehensive production setup for a FastAPI application:
# production.py
import multiprocessing
import uvicorn
import os
if __name__ == "__main__":
# Determine number of workers based on CPU cores
workers_count = (multiprocessing.cpu_count() * 2) + 1
# Get port from environment variable or use default
port = int(os.environ.get("PORT", 8000))
# Configure Uvicorn with production settings
uvicorn.run(
"main:app",
host="0.0.0.0",
port=port,
workers=workers_count,
log_level="info",
access_log=True,
limit_concurrency=1000,
timeout_keep_alive=5,
# Enable HTTPS if certificates are available
ssl_keyfile=os.environ.get("SSL_KEYFILE"),
ssl_certfile=os.environ.get("SSL_CERTFILE"),
# Disable reload in production
reload=False
)
Real-World Application: API Service with Health Checks
Here's a more practical example of a FastAPI application with health checks and Uvicorn configuration:
# main.py
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.middleware.cors import CORSMiddleware
import psutil
import os
app = FastAPI(title="Production API Service")
# Add CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Lock this down in production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Health check endpoint for load balancers
@app.get("/health")
async def health_check():
# Check system resources
memory = psutil.virtual_memory()
disk = psutil.disk_usage('/')
return {
"status": "healthy",
"system": {
"memory_usage": f"{memory.percent}%",
"disk_usage": f"{disk.percent}%",
"cpu_usage": f"{psutil.cpu_percent()}%"
}
}
@app.get("/api/data")
async def get_data():
return {"data": "This is your API data"}
# server.py
import uvicorn
import os
if __name__ == "__main__":
# Get configuration from environment variables (for containerized deployments)
host = os.getenv("HOST", "0.0.0.0")
port = int(os.getenv("PORT", 8000))
workers = int(os.getenv("WORKERS", 1))
# Run with proper production settings
uvicorn.run(
"main:app",
host=host,
port=port,
workers=workers,
log_level="info",
access_log=True,
limit_concurrency=1000,
timeout_keep_alive=5
)
Docker Deployment Example
When deploying with Docker, you can configure Uvicorn like this:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# Set environment variables
ENV HOST=0.0.0.0
ENV PORT=8000
ENV WORKERS=4
# Run Uvicorn with the proper configuration
CMD ["python", "server.py"]
Docker compose example:
version: '3'
services:
fastapi:
build: .
ports:
- "8000:8000"
environment:
- HOST=0.0.0.0
- PORT=8000
- WORKERS=4
- LOG_LEVEL=info
restart: always
Summary
Proper Uvicorn configuration is critical for deploying FastAPI applications that are stable, secure, and performant. Key takeaways include:
- Use multiple workers in production (2 × CPU cores + 1)
- Configure proper timeouts to protect against slow clients
- Set appropriate concurrency limits based on your server's capacity
- Enable HTTPS for production deployments
- Configure logging for monitoring and debugging
- Use environment variables for flexible configuration
By mastering Uvicorn configuration, you can ensure your FastAPI applications perform optimally under various load conditions and deployment environments.
Additional Resources
Exercises
- Create a FastAPI application and configure Uvicorn to run it on port 9000 with debug-level logging.
- Modify the application to use HTTPS with self-signed certificates.
- Create a production-ready configuration that automatically detects the number of CPU cores and sets the appropriate number of workers.
- Implement a custom logging configuration that rotates log files daily.
- Create a Docker container that runs your FastAPI application with optimal Uvicorn settings.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)