Skip to content

Uvicorn vs Gunicorn

  • Uvicorn is an ASGI (Asynchronous Server Gateway Interface) server implementation for Python.
  • Designed to work with async web frameworks like FastAPI, Starlette, and Django 3.0+.
  • ASGI server - Built for asynchronous Python web applications
  • Lightweight - Minimal overhead, designed for high performance
  • Single-process - Typically runs as a single process (though can be scaled)
  • Development-friendly - Excellent for development with auto-reload feature
  • Built for async - Natively supports async/await patterns
Terminal window
# Development with auto-reload
uvicorn main:app --reload
# Production with specific settings
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

Gunicorn (Green Unicorn) is a WSGI (Web Server Gateway Interface) server for Python. It’s a pre-fork worker model server that’s been widely used for traditional synchronous Python web applications.

  • WSGI server - Originally designed for synchronous frameworks
  • Process manager - Manages multiple worker processes
  • Production-ready - Battle-tested for production deployments
  • Load balancing - Distributes requests among worker processes
  • Mature ecosystem - Extensive configuration options
Terminal window
# Basic usage with synchronous workers
gunicorn main:app --workers 4
# With specific bind settings
gunicorn main:app --bind 0.0.0.0:8000 --workers 4
AspectUvicornGunicorn
InterfaceASGI (Asynchronous)WSGI (Synchronous)
Primary UseAsync frameworks (FastAPI, Starlette)Sync frameworks (Flask, Django)
ArchitectureCan run standalone or as workerProcess manager with worker processes
PerformanceBetter for I/O-bound async appsBetter for CPU-bound sync apps
Worker TypesUvicorn workers (async)Sync, async, gevent, etc. workers
Learning CurveSimpler for async appsMore configuration options

Uvicorn (Standalone ASGI):

Client Request → Uvicorn Process → Async Application

Gunicorn with Uvicorn Workers:

Client Request → Gunicorn Master → Multiple Uvicorn Workers → Async Application
  • Development environment - Fast reload, easy debugging
  • Simple deployments - Less complex configuration needed
  • Small to medium traffic - When you don’t need advanced process management
  • Testing/POC - Quick setup and iteration
  • Production environment - Need process management and monitoring
  • High traffic - Better load distribution and fault tolerance
  • Zero-downtime deployments - Graceful worker reloads
  • Advanced configuration - Need fine-tuned performance settings
# Example: FastAPI app performance comparison
"""
Uvicorn alone:
- Lower memory overhead per request
- Better for long-lived connections (WebSockets)
- Optimal for pure async workloads
Gunicorn + Uvicorn workers:
- Better CPU utilization on multi-core systems
- Process isolation (if one worker crashes, others survive)
- Better for mixed async/sync workloads
"""

Uvicorn (Recommended for development):

Terminal window
# Development with auto-reload
uvicorn main:app --reload --host 0.0.0.0 --port 8000
# With debug level logging
uvicorn main:app --reload --log-level debug

Option A: Uvicorn Alone (Simpler)

Terminal window
# Multiple workers with uvicorn
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
# With process manager like supervisord or systemd

Option B: Gunicorn + Uvicorn Workers (Recommended for production)

Terminal window
# Install required packages
pip install gunicorn uvicorn
# Run with uvicorn workers
gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
# With more configuration
gunicorn main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--timeout 120 \
--keep-alive 5 \
--max-requests 1000 \
--max-requests-jitter 100

Uvicorn Configuration (uvicorn_config.py):

import uvicorn
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
workers=4,
reload=False, # Disable in production
log_level="info"
)

Gunicorn Configuration (gunicorn_conf.py):

# gunicorn_conf.py
import multiprocessing
# Server socket
bind = "0.0.0.0:8000"
# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "uvicorn.workers.UvicornWorker"
# Timeout
timeout = 120
keepalive = 5
# Logging
accesslog = "-"
errorlog = "-"
loglevel = "info"
# Process naming
proc_name = "fastapi_app"
Terminal window
# Use uvicorn with reload for development
uvicorn main:app --reload --host localhost --port 8000
Terminal window
# Use gunicorn with uvicorn workers for production
gunicorn main:app \
--workers 4 \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:8000 \
--timeout 120
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Use gunicorn as entrypoint for production
CMD ["gunicorn", "main:app", "--workers", "4", "--worker-class", "uvicorn.workers.UvicornWorker", "--bind", "0.0.0.0:8000"]
# Optimal worker calculation
import multiprocessing
# General formula
workers = multiprocessing.cpu_count() * 2 + 1
# For I/O heavy apps (more workers)
workers = multiprocessing.cpu_count() * 3
# For CPU heavy apps (fewer workers)
workers = multiprocessing.cpu_count() + 1
Terminal window
# Direct uvicorn (simpler)
uvicorn main:app --workers 2 --host 0.0.0.0 --port 8000
Terminal window
# Gunicorn + Uvicorn workers (recommended)
gunicorn main:app --workers 8 --worker-class uvicorn.workers.UvicornWorker

Scenario 3: High Traffic with Load Balancer

Section titled “Scenario 3: High Traffic with Load Balancer”
Terminal window
# Multiple instances behind load balancer
gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker
# Run multiple instances on different ports
Terminal window
# Basic monitoring needed
# Use process manager like supervisord or systemd
Terminal window
# Built-in process management
gunicorn main:app --workers 4 --preload # Preload app before forking
# Graceful reload
kill -HUP <master_pid>

Uvicorn is your go-to for development and simpler async applications, while Gunicorn + Uvicorn workers provides a robust production-grade solution for high-traffic deployments. The combination gives you the best of both worlds: Gunicorn’s process management and Uvicorn’s async capabilities.

For most FastAPI applications in production, the recommended approach is:

Terminal window
gunicorn main:app --workers 4 --worker-class uvicorn.workers.UvicornWorker