Skip to main content

Deploy FastAPI on Cloudflare Workers: the packages pip won't install

Deploy FastAPI on Cloudflare Workers now that Python is GA: what pip installs cleanly, what needs a Wasm build, and the C-extension wall in between.

7 min read
Four-stage pipeline showing a Cloudflare Python Worker request flowing from the edge through Pyodide to FastAPI and out through R2, D1, and Queues bindings

TL;DR: Cloudflare’s Python Workers went generally available on September 21, 2026, and you can now deploy FastAPI on Cloudflare Workers with the same ASGI app you already run, no JavaScript glue code required for bindings. Django, Flask, and socket-based drivers like aiomysql and asyncpg work today too — but any package with a native C, C++, or Rust extension (numpy, pandas, cryptography) still needs a Wasm-targeted wheel built with cibuildwheel before Pyodide will import it.

That’s the whole story compressed into two sentences, and it’s also the part every announcement post skips. This is the deploy guide: what actually runs today, what the GA release changed under the hood, and exactly which packages will fail your build — and why.

What Is a Python Worker on Cloudflare?

A Python Worker is a Cloudflare Worker whose runtime interprets Python instead of JavaScript, using Pyodide — a WebAssembly build of CPython that Cloudflare runs inside the same V8 isolate that received the HTTP request. There’s no separate Python process, no container, and no cold database pool to boot: the interpreter loads inside the isolate the way a JavaScript bundle would, and your app code (WSGI or ASGI) runs from there.

Four-stage pipeline diagram showing a Cloudflare Python Worker request moving from the edge, through Pyodide's Wasm sandbox, into an unmodified FastAPI or Django app, and out through R2, D1, and Queues bindings as plain Python dicts with no JavaScript glue code

That single architectural fact is why the deploy story looks nothing like a traditional Python host: you’re not choosing a WSGI server, a process manager, or a container base image. You’re compiling your app into the same edge-function model Cloudflare already runs for JavaScript, with a different interpreter inside it.

Why Python Workers Reaching GA Actually Matters

Cloudflare shipped the Python Workers beta about two years before this GA announcement, and the gap between beta and GA is the actual news, even though “generally available” is the headline everyone else ran with. Two things changed:

  • The JavaScript interop glue is gone. Sending a plain dict to a binding used to require to_js(payload, dict_converter=js.Object.fromEntries) — a line of JS-interop boilerplate wedged into every Python handler that touched a Queue, a Durable Object, or Workers AI. GA accepts the dict directly.
  • Raw TCP sockets showed up. The beta had no path to a live socket, so any driver or HTTP client that opens one at a low level simply didn’t work. GA adds that support, which is the specific thing that unblocks aiomysql and asyncpg over a Hyperdrive binding.

Neither change touches performance or pricing — Cloudflare’s post doesn’t publish a single cold-start number, a memory ceiling, or a request-cost figure, and you should be suspicious of anyone who quotes one as fact. What did change is friction: code that used to need a JavaScript-shaped workaround now reads like ordinary Python.

Side-by-side code comparison showing the Python Workers beta requiring explicit to_js JavaScript glue code with no socket support, next to the GA release accepting a plain Python dict directly with working aiomysql and asyncpg drivers over Hyperdrive

There’s also a quieter reason this matters if you’re doing anything AI-shaped: Cloudflare explicitly supports openai, langchain, and mcp inside a Python Worker, plus a langchain-cloudflare integration for wiring straight into Workers AI and Vectorize. If your stack already leans on LLM engineering patterns built in Python — chains, tool-calling agents, retrieval pipelines — you can now run that orchestration layer at the edge instead of proxying it through a separate origin.

How Do You Deploy FastAPI on Cloudflare Workers?

The deployment mechanics don’t change from what Cloudflare has supported since the beta — GA doesn’t add new config surface, it removes friction from the surface that was already there.

Deploying FastAPI in five steps

  1. Point wrangler.toml at Python. Set main = "src/app.py" and add compatibility_flags = ["python_workers"] — that’s the flag that tells Wrangler this entry point is Python, not JavaScript.
  2. Write the ASGI adapter, not a new app. Your existing FastAPI() instance is the same object you’d run anywhere; the Worker’s on_fetch handler bridges the Workers Request/Response into the ASGI calling convention FastAPI already expects.
  3. List pure-Python dependencies in requirements.txt. FastAPI, Starlette, and Pydantic all ship a none-any wheel, so they install exactly the way they would on any other host.
  4. Bind Hyperdrive before you touch a database driver. asyncpg and aiomysql need the raw-socket support GA added — route them through a Hyperdrive binding instead of a direct TCP connection string.
  5. Run wrangler deploy. The Worker ships to the same 300+ edge locations and the same run_worker_first routing model as a JavaScript Worker, because nothing about the deployment topology changed — only the interpreter inside it did.

If you’ve already deployed an MCP server on Cloudflare Workers, step 1 will look familiar — it’s the same wrangler.toml-first mental model, just with a Python entry point instead of a TypeScript one. And if that Worker needs to sit behind something other than the open internet, gating it with Cloudflare Access works identically regardless of which language wrote the handler.

Which Packages Actually Work in Python Workers?

This is the part the GA announcement undersells: whether a package works has nothing to do with how popular it is and everything to do with how it was built.

Package categoryExamplesWorks in a Python Worker today?
Pure-Python wheelFastAPI, Django, Flask, langchain, mcpYes — unmodified
Socket-based driveraiomysql, asyncpg (via Hyperdrive)Yes — new in this GA release
C / C++ / Rust extensionnumpy, pandas, cryptographyNo — needs a cibuildwheel-built Wasm wheel first

Package compatibility table showing pure-Python wheels like FastAPI and Django working today, socket-based drivers like aiomysql and asyncpg newly working over Hyperdrive, and C, C++, and Rust extension packages like numpy and pandas needing a cibuildwheel Wasm build before they import

The dividing line is the wheel format, not the package name. A pure-Python package publishes a none-any wheel — the exact same bytes install on Linux, macOS, Windows, or Pyodide’s Wasm target, because there’s no compiled code inside it to target a platform. A package with a C, C++, or Rust extension publishes a separate compiled wheel per platform, and until this year almost none of them published one for Pyodide’s pyemscripten target.

What Breaks: C Extensions and the Wasm Wall

Here’s the failure mode you’ll actually hit: you pip install numpy (or it’s a transitive dependency of something you do want), and the install step can’t find a matching wheel for the Wasm platform Pyodide runs on. It isn’t a bug in your code — it’s that nobody has cross-compiled that exact version of that exact package to pyemscripten yet.

Two ways out, in order of how much work they are:

  1. Drop the dependency if you can. Most Workers don’t need numpy-grade numerical code; if you’re doing light aggregation, plain Python or a pure-Python alternative is often enough, and it sidesteps the wall entirely.
  2. Build it yourself with cibuildwheel. Cloudflare’s post points at cibuildwheel as the tool that cross-compiles a C-extension package to a Wasm wheel. It’s real work — you’re building a package maintainer’s release pipeline for one dependency — but it’s the documented path, and PEP 783 exists specifically to standardize the pyemscripten platform tag so more maintainers ship one by default instead of every consumer building their own.

If your app leans on heavier ML inference rather than a general-purpose numerical library, don’t fight this wall at all — hand that workload to Workers AI instead of trying to shoehorn a model-serving stack’s C extensions into Pyodide.

Best Practices for Running Python Workers in Production

  • Treat “it’s Python” as an interface, not a guarantee. Audit requirements.txt for C-extension packages before you commit to Workers, not after a deploy fails.
  • Route every database connection through Hyperdrive. It’s the supported path for the sockets GA unlocked, with connection pooling built in.

That covers dependencies and data access. The rest is about not over-trusting the “it’s just Python” framing:

  • Test the real wrangler deploy path, not just a local pip install. A wheel installing on your laptop says nothing about whether a pyemscripten wheel exists.
  • Reach for Workers Workflows for anything long-running. A Python Worker is still request/response under the hood; durable jobs belong in Cloudflare Workflows.
  • Don’t assume performance parity with a JS Worker until you’ve measured it. Cloudflare hasn’t published comparative numbers, and Pyodide adds real interpretation overhead.

If your app also calls out to OpenAI’s SDK, the version pin matters as much as the platform does — see the httpx2 migration guide for the breaking change that trips up exactly this kind of Python service.

FAQ

Can I run FastAPI on Cloudflare Workers today? Yes. FastAPI ships as a pure-Python wheel and runs unmodified inside Pyodide. You write the same ASGI app you’d deploy anywhere else and point wrangler.toml at it — no rewrite required.

Does numpy or pandas work in a Python Worker? Not as a plain pip install. Both ship native C extensions, and Pyodide only runs packages built for its Wasm target. Someone has to cross-compile them with cibuildwheel into a pyemscripten wheel first.

What actually changed when Python Workers went GA? Bindings now accept plain Python dicts instead of the to_js() glue the beta needed, and Cloudflare added raw TCP socket support so aiomysql and asyncpg can reach a database over Hyperdrive.

Is a Python Worker slower than a JavaScript Worker? Cloudflare hasn’t published cold-start or latency numbers for this release. Pyodide adds an interpretation layer a JS Worker doesn’t have — benchmark your own workload rather than trusting an unsourced number.

Can I use LangChain or the OpenAI SDK in a Python Worker? Yes — Cloudflare explicitly lists openai, langchain, and mcp as supported, plus a langchain-cloudflare integration for Workers AI and Vectorize.

Do I need to rewrite my FastAPI app to deploy it on Workers? No — the app code keeps the same ASGI interface. You add a wrangler.toml entry point, and you need a plan for any C-extension dependency: drop it or get a Wasm build.

Sources

Frequently asked questions

Share this article:
X LinkedIn

Google Search · Preferred sources

Prefer this site on Google

If you already read this writing, add umesh-malik.com as a Preferred Source. Google can then highlight it with a preferred badge in Top Stories, AI Overviews, and AI Mode — for you, not as a site-wide ranking boost.

Keep reading

Get new posts on AI, Claude Code & LLMs

New deep-dives on AI engineering, Claude Code, and developer tooling — follow along however you prefer.