All notes
Deployment Concept

How a Python web service actually runs

Trace a request through the proxy, application server, and application. Then examine workers, shared state, and streaming connections.

Follow a request

A web framework defines application behavior. An application server accepts network connections and invokes that behavior. A reverse proxy can sit in front to handle routing, HTTPS, or other edge responsibilities.

Browser
   │ HTTPS
   ▼
Reverse proxy or managed load balancer
   │ HTTP
   ▼
ASGI server → Python application → Database / external service

FastAPI is a framework. Uvicorn is an ASGI application server. Nginx and Caddy can act as reverse proxies. A cloud platform may provide the proxy role without requiring a separately managed proxy installation.

What ASGI means

ASGI defines an interface between a Python server and an application. It supports asynchronous interaction and connection types such as WebSockets. WSGI defines the older synchronous request-response interface.

These interfaces operate at a different layer from the HTTP connection between a browser and a server. Using ASGI also does not make every operation in application code nonblocking.

An illustrative FastAPI application might expose a simple liveness response:

from fastapi import FastAPI

app = FastAPI()

@app.get("/health")
def health():
    return {"status": "ok"}

An ASGI server imports the application object and serves requests. A development reloader restarts it after source changes; production instead needs an explicit service lifecycle and recovery policy.

A running process is one part of a service

Responsibility Typical owner
Routing and request validation Web application
HTTP connections and application invocation ASGI server
HTTPS and edge routing Proxy or managed platform
Process startup and restart Service manager or container platform
Release validation and rollout Deployment pipeline
Durable records and job state Database, queue, or other shared service

Uvicorn can manage multiple worker processes. Some deployments use a separate worker manager. A particular process manager is not a universal prerequisite for serving a Python application.

Workers do not share ordinary Python memory

Consider an application that stores job status in a module-level dictionary:

Create job → Worker A → Status saved in A's memory
Read job   → Worker B → Job may not be present

Each worker has its own memory. Restarting the process also discards that state. Adding replicas can expose the same problem across containers.

Use a shared persistence layer when status must survive requests, process changes, or restarts. Long-running jobs may also need a queue, retry policy, idempotency, cancellation, and durable execution records.

Starting a background task in an API process does not create a durable job system. A single worker avoids cross-worker memory differences but does not provide recovery after a crash.

Streaming adds lifecycle requirements

Server-Sent Events send events over a continuing HTTP response. A buffering proxy can delay delivery, while idle timeouts can close connections.

Check buffering, timeouts, heartbeats, reconnection, and event replay requirements. During a release, define what happens to existing streams and long-running work before terminating the old process.

Forwarded headers also need a trust boundary. An application should accept client-address and protocol information only from the proxies it is configured to trust.

Verify the behavior users need

A fixed /health response confirms that a request reached a responding application. It does not prove that the database, job processing, or streaming path works.

Distinguish basic liveness from readiness to receive traffic. Test a representative workflow, observe failures and resource use, and verify restart behavior before adding more workers.

References