Python Workers Are GA. Uvicorn Did Not Come With Them

Cloudflare's Sep 21 post: Python Workers are generally available. FastAPI and Django run through ASGI/WSGI. Bindings dropped the to_js glue. Hyperdrive talks SQL.

GA means the fetch handler speaks Python. It does not mean you brought Gunicorn with you.

Cloudflare’s September 21 post is the product sentence: Python Workers are generally available. First-class on the Developer Platform. Bindings to Workers AI, R2, D1, Hyperdrive, Durable Objects, Queues, and Workflows. FastAPI, Django, and Flask inside a Worker. You can even spin a Python Worker from another Worker with Dynamic Workers.

Technobezz the same day is the translation for people who still think “Worker” means a fetch in JavaScript. The platform load-balances. Uvicorn is not invited.

We already did the FastAPI vs Django vs Flask landscape and a Django survey that was fine with the monolith. This week is not a framework war. It is where the ASGI app runs.

What GA is not

Python on Workers was already a preview story. GA, in Cloudflare’s words, is that you can bring the code, libraries, and patterns you already know and hook them to the rest of the platform without treating Python as a guest.

The interpreter is still Pyodide: a Wasm build of Python, sitting on a runtime that has had WebAssembly since 2018. That sentence should kill two fantasies. One, that this is CPython on a VM you SSH into. Two, that Cloudflare invented a new Python. They put the interpreter they could ship into the isolate they already had.

If your app assumes a writable local disk, a long-lived process, and pip install of a binary wheel that never heard of Wasm, GA did not fix you. If your app is an ASGI handler plus a database and a queue, this post is for you.

Dynamic Workers — a Python Worker created inside another Worker — is a platform trick, not a reason to rewrite the monolith. File it next to “we can fan out,” then go back to the handler.

The glue code is the DX

The old path for a Queue send, as Cloudflare reprints it, looked like this:

from pyodide.ffi import to_js
import js

self.env.QUEUE.send(to_js({"key": "value"}, dict_converter=js.Object.fromEntries))

That is not Python. That is a peace treaty with JavaScript objects at the RPC boundary. The GA pitch is that the runtime and the Python SDK now take a dict. Technobezz notes Cloudflare is selling that as fewer errors for humans and for AI agents that write Workers.

I believe the human part. The agent part is a vendor hope. Either way, deleting to_js is the commit you wanted.

Native bindings are the rest of the list: Workers AI, R2, D1, Hyperdrive, Durable Objects, Queues, Workflows. You still have to know what those products are. You no longer have to pretend they are TypeScript to talk to them.

The FastAPI snippet in the announcement is the new hello world. Import asgi and WorkerEntrypoint from workers. Build a FastAPI app. Pull env off request.scope["env"]. Call env.AI.run with @cf/openai/gpt-oss-120b. Export Default = asgi.entrypoint(app).

That last line is the replacement for Uvicorn. The Worker is the server.

ASGI and WSGI are the compatibility story

Cloudflare says workers.asgi covers async frameworks and workers.wsgi covers sync ones such as Django. The connectors turn incoming requests into the structures those interfaces already expect. FastAPI, Django, and Flask are the named three. The text also says any framework on WSGI or ASGI.

That is a bigger deal than a Flask demo. It means you can keep the routing style your team already reviews. It does not mean every middleware you bolted onto Gunicorn will behave. Process-level assumptions will still fail. File-watchers will still fail. Anything that wanted a sidecar on localhost should be a binding instead.

If you have been pushing HTMX on a Python server to avoid a SPA, a Worker is a reasonable place to put that stack: HTML out, SQL through Hyperdrive, no Node build. If you have a Django admin that assumes a persistent process and a local media disk, stay off this until you have a plan for both.

Starlette users should keep the BadHost CVE sitting on CISA KEV in the same mental folder. A new host is not a new security story. FastAPI on Workers still inherits whatever you imported.

Hyperdrive is how SQL gets into the isolate

The announcement’s database path is Hyperdrive for PostgreSQL and MySQL. The printed example uses aiomysql.connect with host, port, user, password, and database from self.env.HYPERDRIVE_MYSQL, ssl=None. Cloudflare points at docs for which packages are supported. I am not going to invent that list from memory.

What you can say from the post: you use the drivers you already know, pointed at Hyperdrive’s connection info, not at a public Postgres hostname you stuffed into a secret. The Worker still should not be holding a long conversation with a primary in another continent without that pooling layer. That is why Hyperdrive is in the GA story and psycopg2 against a garage Raspberry Pi is not.

D1 remains the SQLite-shaped option on the same platform. R2 is the bucket. Queues are the buffer. None of that is new. What is new is calling them without a JS conversion step.

If your current FastAPI app is “Uvicorn + Postgres + Redis,” map it before you cut over. Postgres may be Hyperdrive. Redis may be a Durable Object or a Queue, or it may be a reason to stay on a container. GA does not create a Redis binding just because you wanted one in the sentence.

What I would actually ship this week

A small FastAPI service that already talks HTTP and a SQL database, with no native extensions, is the candidate. Copy it. Swap the entrypoint. Point the database at Hyperdrive in a staging account. Hit the AI binding only if you needed an LLM in-process anyway.

Leave the Django monolith where it is unless you already split a read-only surface that can live without local files. Flask scripts that are really CLIs do not belong here.

Keep an eye on package support. Pyodide is not PyPI. If the first import is a binary wheel that never built for Wasm, the GA blog will not help you.

Do not treat Dynamic Workers as a service mesh. Do not treat Workers AI as a reason to abandon a model host you already trust. The @cf/openai/gpt-oss-120b string in the sample is an example, not a recommendation.

For teams that already deploy JS Workers, the internal win is one platform, two languages. For teams that only know Render and a Procfile, this is a new set of limits, not a cheaper Heroku.

The server is the platform now

I like that the post is honest about the history. Wasm in 2018. Pyodide as the interpreter they could actually run. Glue code that was embarrassing. GA as the point where they will take your ticket.

I do not like the implied sentence that “Python web apps” as a category just moved to the edge. They did not. A slice of ASGI apps that can live in an isolate moved to the edge.

If you try this, keep Uvicorn in the repo until the Worker path has been in production for a month. Rollback is a skill. The announcement is a changelog.

The hidden work is packaging. Pyodide will import a lot of pure Python. It will not import every wheel your laptop has. Pin a tiny requirements set and try it in a throwaway Worker before you move a service that already has twenty native dependencies. Cloudflare’s own Hyperdrive note is the model: they tell you which packages are supported instead of pretending PyPI is the runtime. Copy that honesty into your README.

Cold starts and CPU time are the next conversation after “it imported.” A FastAPI app that does a 200ms SQL query and returns JSON is in-bounds. A FastAPI app that loads a pandas pipeline into memory on every request is a container that wandered into the wrong product page. Durable Objects exist if you need coordination. They are not a replacement for a worker process you left running overnight.

Auth is still your problem. The ASGI connector gives you a request. It does not give you Django’s entire session stack for free, and it does not give you FastAPI users if you never configured them. Put the identity binding you already trust in front, or keep the app behind Cloudflare Access, and do not assume request.scope["env"] is a security boundary.

Logs will feel worse than Gunicorn plus stdout on a VM. That is not a reason to stay on the VM forever. It is a reason to emit structured lines on day one, with a request id, before you debug a 500 that only happens at the edge.

If the team is already on JS Workers, write the Python service next to an existing Worker that handles the public hostname, and only cut the route when the Python path has metrics. Dual-running is dull. Dual-running is how you avoid a Friday incident that exists only because GA felt like permission.

A note on the AI binding, because the sample will tempt people. env.AI.run with a model string is a product demo. It is not an argument that your inference should live in the same isolate as your SQL. If the model call is slow or billed, keep it behind a queue. If you already have a Python client for a lab API, GA did not make Workers AI mandatory. It made the import path less ugly.

Django people will ask about migrations and collectstatic. Those are still batch jobs. Run them where you already run them. The Worker is the request path. Mixing a migrate into a fetch handler because “it’s Python now” is how you lock Hyperdrive and surprise yourself.

Flask people will ask about app.run(). That is the Uvicorn problem in a smaller font. Export the WSGI entrypoint. Let the platform take the port. If your Flask app is a folder of scripts with a if __name__ block and a sqlite file beside it, this is not your host.

The GA post is dated September 21. That is six days ago. Docs will move. Package support will move. Write your first Worker as if the snippet in the blog will be wrong in a month, and keep the link to the Hyperdrive Python page next to the code, not in a slide.

Spread The Article

Share this guide

Send this article to your network or keep a copy of the direct link.

X Facebook LinkedIn Reddit Telegram

Discussion

Leave a comment

No comments yet

Be the first to start the conversation.