Deploy FastAPI on Cloudflare Python Workers (2026)

Web Development Sep 23, 2026

Watch on YouTube

Deploy FastAPI on Cloudflare Python Workers (2026)

Deploy FastAPI on Cloudflare Python Workers (2026) — Cloudflare declared Python Workers generally available on 21 Sep 2026. You can now run FastAPI at the edge with the built-in workers.asgi bridge, manage deps with uv + pywrangler, and reach Workers AI, Hyperdrive, R2, and the rest of the platform without a separate uvicorn host. This guide is a working quickstart plus optional static frontend and one AI call.

PyInns already covers same-process SPA serving with FastAPI app.frontend(), containerized production with FastAPI + Docker + PostgreSQL, and streaming LLM tokens with FastAPI SSE. Those stay on classic hosts. This post is the Workers path: ASGI entrypoint, wrangler.jsonc, and edge bindings.

TL;DR

  • Python Workers GA (Sep 2026): FastAPI via from workers import asgi then Default = asgi.entrypoint(app)
  • wrangler.jsonc: compatibility_flags: ["python_workers"], compatibility_date near today
  • pyproject.toml: requires-python >=3.13, fastapi; dev group workers-py, workers-runtime-sdk
  • Local: uv run pywrangler dev → curl http://localhost:8787/
  • Optional: Static Assets binding + catch-all (Workers equivalent of app.frontend()); Workers AI via request.scope["env"].AI.run; Hyperdrive + aiomysql / asyncpg for TCP DBs

Why Workers instead of uvicorn + Docker?

ConcernClassic FastAPI (uvicorn/Docker)Python Workers
Process model Long-lived ASGI server you operate Request-scoped Worker; Cloudflare runs the ASGI bridge
Cold path / scale You size replicas and health checks Global edge isolate; pay for CPU time
Bindings Env vars + your own clients Native env (AI, Hyperdrive, R2, D1, Queues, …)
Static SPA app.frontend() / disk StaticFiles Assets binding + catch-all proxy (files not in the Worker bundle)
Python version Whatever you install Workers expect >=3.13 in current docs

Keep Docker + Postgres when you need heavyweight local disks, long-lived WebSocket fan-out you already operate, or drivers Workers still do not support. Use Workers when you want FastAPI next to Cloudflare bindings with minimal ops.

Prerequisites

  • Node tooling for Wrangler (via workers-py / pywrangler)
  • uv installed
  • A Cloudflare account (deploy later; this tutorial stops at local pywrangler dev)

Official package page (paraphrased here; always re-check dates/flags): FastAPI on Cloudflare Workers. GA announcement: Python Workers are now generally available.

Minimal project layout

.
├── pyproject.toml
├── wrangler.jsonc
└── src
    └── main.py

1. FastAPI app + ASGI entrypoint

FastAPI speaks ASGI. On Workers you do not start uvicorn yourself — you hand the app to the runtime bridge. The short form uses asgi.entrypoint. The longer form subclasses WorkerEntrypoint and calls asgi.fetch when you need more control over env.

# src/main.py
from fastapi import FastAPI
from workers import asgi

app = FastAPI(title="PyInns Cloudflare Workers demo")

@app.get("/")
def read_root() -> dict[str, str]:
    return {"Hello": "World"}

@app.get("/api/health")
def health() -> dict[str, str]:
    return {"status": "ok"}

# Short form (recommended for most apps)
Default = asgi.entrypoint(app)

# Equivalent long form if you need the class:
# from workers import WorkerEntrypoint
# class Default(WorkerEntrypoint):
#     async def fetch(self, request):
#         return await asgi.fetch(app, request, self.env)

The exported name Default is the Worker entrypoint Wrangler loads. Do not omit it — without an entrypoint the isolate has nothing to call.

2. wrangler.jsonc

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "my-fastapi-app",
  "main": "src/main.py",
  // Keep this near deploy day; Workers gates features on the date
  "compatibility_date": "2026-09-23",
  "compatibility_flags": ["python_workers"]
}

The python_workers flag is required. Bump compatibility_date when you intentionally adopt newer runtime behavior — do not copy an ancient date from a blog sample without checking.

3. pyproject.toml

[project]
name = "my-fastapi-app"
version = "0.1.0"
description = "FastAPI on Cloudflare Python Workers"
requires-python = ">=3.13"
dependencies = [
    "fastapi",
]

[dependency-groups]
dev = [
    "workers-py",
    "workers-runtime-sdk",
]

workers-py gives you the pywrangler CLI wrapper; workers-runtime-sdk provides the in-Worker workers module types/runtime pieces used locally. Add only packages that Workers actually support (pure Python / available Pyodide or PyEmscripten wheels) — see Cloudflare’s supported packages list before pinning heavy native wheels.

4. Run locally

uv sync
uv run pywrangler dev

# other terminal
curl http://localhost:8787/
# {"Hello":"World"}

curl http://localhost:8787/api/health
# {"status":"ok"}

Default local port is 8787 (Wrangler). If the port is taken, check the pywrangler / Wrangler banner for the actual URL.

Optional — static frontend via Assets (Workers app.frontend)

On a normal VPS, app.frontend() reads files from disk. On Workers, put the SPA build in something like ./public/ and bind Cloudflare Static Assets so the files live in the global asset store — not inside the Worker bundle.

{
  "name": "my-fastapi-app",
  "main": "src/main.py",
  "compatibility_date": "2026-09-23",
  "compatibility_flags": ["python_workers"],
  "assets": {
    "directory": "./public/",
    "binding": "ASSETS",
    "run_worker_first": true
  }
}

run_worker_first: true sends every request to FastAPI first so /api/... wins. Then add a catch-all that proxies unmatched paths to the binding:

# append to src/main.py (after API routes; catch-all last)
from fastapi import Request
from fastapi.responses import Response

@app.get("/api/hello")
async def api_hello() -> dict[str, str]:
    return {"message": "Hello from the API"}

@app.get("/{path:path}")
async def frontend(path: str, request: Request):
    env = request.scope["env"]
    asset_url = f"https://assets.local/{path}"
    resp = await env.ASSETS.fetch(asset_url)
    body = await resp.bytes()
    return Response(content=body, status_code=resp.status, headers=resp.headers)

Drop a built index.html (and hashed JS/CSS) under public/. /api/hello stays JSON; /index.html and other asset paths come from ASSETS. More detail: Workers Static Assets.

Optional — one Workers AI call

GA also made common AI libraries (including openai, LangChain, and MCP clients) workable on the same runtime, and ships langchain-cloudflare when you want chains aimed at Workers AI. The smallest practical demo uses the binding directly:

from fastapi import FastAPI, Request
from workers import asgi

app = FastAPI()

@app.get("/api/ai-ping")
async def ai_ping(request: Request):
    env = request.scope["env"]
    # Model id will change — pick a current Workers AI model from the dashboard/docs
    result = await env.AI.run(
        "@cf/meta/llama-3.1-8b-instruct",
        {
            "messages": [
                {"role": "system", "content": "Reply in one short sentence."},
                {"role": "user", "content": "Say hello from a Python Worker."},
            ]
        },
    )
    return {"result": result}

Default = asgi.entrypoint(app)

Enable Workers AI on the Worker (Wrangler ai binding / dashboard) before calling env.AI. For token streaming over HTTP on a classic host, reuse the patterns in the SSE LLM guide — wire the generator to Workers AI only after you confirm streaming support for your chosen model API.

Databases — Hyperdrive + TCP drivers (brief)

Before GA, Python Workers lacked TCP sockets, so typical MySQL/Postgres drivers did not work. With the Workers Connect API and Hyperdrive, documented paths include aiomysql and asyncpg through a Hyperdrive connection string. Configure a Hyperdrive binding in Wrangler, read it from request.scope["env"], and use the async driver Cloudflare documents for that binding — do not assume every PyPI DB package is available. For a full Docker Compose Postgres stack on your own VMs, stick with the Docker + PostgreSQL production setup.

Deploy checklist (when you are ready)

  • uv run pywrangler deploy (or the Wrangler deploy flow your team standardizes)
  • Confirm compatibility_flags and compatibility_date in production config
  • Bind only what you use (AI, Hyperdrive, Assets, secrets)
  • Smoke-test /api/health and any catch-all frontend paths on the *.workers.dev URL

This draft stops short of production deploy steps on purpose — validate locally first.

Wrap-up

Python Workers GA turns FastAPI into an edge-native option: export Default = asgi.entrypoint(app), set python_workers in wrangler.jsonc, pin Python >=3.13 with fastapi + the workers dev tools, and iterate with uv run pywrangler dev. Add Static Assets when you need the Workers equivalent of app.frontend(), call Workers AI from request.scope["env"] for inference next to your routes, and use Hyperdrive when you need MySQL/Postgres over TCP. Pair this with PyInns’ SPA, Docker/Postgres, and SSE posts depending on whether you stay on classic hosts or move the API to the edge.

Share:
Last updated: September 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices