Skip to content

Web Server and Application Server

James Brucker edited this page Nov 23, 2025 · 2 revisions

The FastAPI back-end service uses asynchronous code and requires an ASGI-capable application server.

In addition, the application as a whole requires a separate (front facing) web server to provide access to static files, TLS, load balancing, and other features needed by scalable web apps.

Purpose and Role

Component Primary Role Use with FastAPI
Gunicorn with Uvicorn workers Python application server Runs the FastAPI app (ASGI).
Uvicorn
Nginx Reverse proxy, static file server, TLS termination Fronts the application server.

Nginx is not an ASGI server, so it cannot run FastAPI directly.

Design

In the simplest practical deployment, we can run Nginx in front of Uvicorn directly:

flowchart LR
    A((Client)) -->|Http Req| N[Nginx]
    N --static file--> A
    N -->|/api| U[Uvicorn]
    U -->P([REST App])
    P <-->D@{shape: cyl, label: "Database"}
Loading

For better CPU utilization and concurrency, we can add Gunicorn as dispatcher for Uvicorn worker threads:

flowchart LR
    A((Client)) -->|Http Req| N[Nginx]
    N --static file--> A
    N -->|/api| G[Gunicorn]
    G -->U1[[Uvicorn worker]]
    G -->U2[[Uvicorn worker]]
    U1 -->P([REST App])
    U2 --> P([REST App])
    P <-->D@{shape: cyl, label: "Database"}
Loading

Uvicorn (ASGI Server)

Strengths:

  • Native ASGI support for async FastAPI
  • Direct Python process execution
  • Optimized for async frameworks
  • WebSocket support
  • Easy deployment with workers

Weaknesses:

  • Limited static file handling
  • Basic load balancing only
  • No native TLS termination
  • Less robust security

Gunicorn

Strengths

  • Native support for ASGI when using uvicorn.workers.UvicornWorker.
  • Designed to manage multiple worker processes.
  • Handles worker lifecycle, graceful reloads, and resource limits.
  • Good performance under high concurrency with proper worker tuning.

Weaknesses

  • Not a full-featured reverse proxy.
  • No native ability to serve static files efficiently.
  • TLS termination must be handled by another component, e.g., Nginx, Traefik, Caddy.

Nginx

Strengths

  • Excellent reverse proxy and load balancer.
  • Efficient static file service.
  • Handles TLS, connection buffering.
  • Rate limiting, security headers.
  • Offloads slow clients from the application server.
  • Very stable under heavy network load.
  • Performs caching & compression

Weaknesses

  • Cannot run Python application directly (no ASGI support).
  • Not a Python process manager.
  • Adds configuration overhead if the environment is simple.
  • Provides no insight into Python worker management or graceful reloads.

Design Decision

Development: Use Nginx with Uvicorn directly. Measure the performance under simulated load.

Deployment: Add Gunicorn with Uvicorn worker threads if there is a measurable improvement in performance.

Example docker-compose snippet

services:
  app:
    build: ./backend
    # Using 'command:' in docker-compose *overrides* the CMD: in the Dockerfile
    # So, this command is in the Dockerfile in the actual code
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
    volumes:
      - ./backend/app:/app/app
      - /tmp:/tmp
    ports:
      - "8000:8000"
    environment:      
      ... (define env vars)

  nginx:
    image: nginx:alpine
    # Map standard HTTP/S ports to high number ports during development
    ports:
      - "8080:80"
      - "8443:443"
    depends_on:
      - app

Clone this wiki locally