Python on Workers catches up: full package support, sub-second warm-ups

When Cloudflare first brought Python to its Workers runtime a year ago, support was deliberately narrow. That changes now. Workers can pull in any package that works with Pyodide — the WebAssembly runtime underneath Python Workers — including all pure-Python libraries and a large share of those that depend on dynamic libraries. Tooling built around uv, the increasingly popular Python package manager, handles dependency resolution and bundling. To keep startup costs in check, Cloudflare also introduced automatic memory snapshots that capture the interpreter after top-level imports, shaving cold start times from roughly 10 seconds to about 1 second for a stack importing fastapi, httpx, and pydantic.

A FastAPI app, up in under two minutes

The workflow starts with uv and npm installed locally. A minimal FastAPI app on Workers is only a few lines of code, and the deploy process resolves dependencies, bundles the Worker, and sends it to Cloudflare’s network — 330 locations across 125 countries. The result runs without any of the usual infrastructure setup, and the free tier still applies: 100,000 requests per day with 10ms of CPU time per invocation.

Once deployed, a Python Worker can do roughly anything an ordinary Python HTTP service can: respond to scheduled cron triggers, participate in WebSocket connections, or lean on Durable Objects for long-running and multi-client state. Cloudflare’s example repository shows edge-rendered HTML with Jinja, dynamic opengraph tag injection, a Durable Objects chat room, consumption of the Bluesky firehose, image generation via Pillow, and RPC from a JavaScript Worker into a Python Worker that loads a Python package.

Why cold starts with packages got fast

Serverless platforms only run code when needed, so an idle Worker may need to boot from scratch when a request arrives. For Python, that overhead was never small — and importing popular libraries adds seconds to the boot on top of a slow interpreter startup. Cloudflare’s first attempt optimized only the interpreter boot; that proved insufficient, since most real workloads import packages during initialization.

Benchmarks that import httpx, fastapi, and pydantic shows the gap:

Platform

Mean Cold Start (secs)

Cloudflare Python Workers

1.027

AWS Lambda (without SnapStart)

2.502

Google Cloud Run

3.069

Cloudflare reports Python Workers cold start at roughly 2.4x faster than AWS Lambda without SnapStart, and 3x faster than Google Cloud Run in these tests. Lambda’s SnapStart — which incurs separate snapshot storage and restore charges — is the option those platforms offer for cutting package-heavy cold starts; Python Workers get the same benefit automatically and at no additional cost. Live benchmark data and methodology notes are available publicly.

That comparison matters less than what is different under the hood. Workers is isolate-based; the long-term roadmap explicitly targets a future with no meaningful cold start at all.

Dependency handling: pywrangler meets uv

Cloudflare chose to build around existing ecosystem tooling rather than improvising a package manager. The new pywrangler command wraps two tools: it invokes uv to install dependencies in a Workers-compatible way, and wrangler for local development and deployment. Concretely, pywrangler reads the Worker’s pyproject.toml, collects the listed dependencies into a python_modules folder inside the Worker, then lets pywrangler dev and pywrangler deploy handle the normal loop of local testing and shipping.

Type hint generation is also part of the toolchain. Running pywrangler types produces type hints for all bindings declared in the wrangler config. To get there, the tool calls wrangler types to emit TypeScript definitions, parses those into an abstract syntax tree, then translates JavaScript-specific concepts — such as iterator fields — into mypy-friendly hints matching Pyodide’s foreign function interface. Pylance and recent mypy versions both understand the output.

Snapshotting: how startup is skipped, not sped up

Rather than optimizing the moment of boot, Workers for Python simply avoids interpreter startup when a new isolate spawns. When a Worker deploys, Cloudflare executes the top-level scope of the code, then captures a memory snapshot and stores it next to the deployed Worker. On a new isolate, that snapshot is restored and the Worker is immediately ready — no Python code runs to get it into that state.

Snapshots at this level of fidelity are practical because the runtime is WebAssembly. The full state lives in Wasm linear memory, which can be saved and restored wholesale without the address space layout randomization concerns that complicate snapshotting native processes.

Randomness must be reseeded

One failure mode of snapshotting is duplicating state that should never be shared, especially entropy. Without care, restored Workers would replay the same “random” numbers across every consumer.

On most systems entropy would be picked up on each process start; a fixed snapshot would lock a single value. All of Python’s entropy sources — getentropy(), getrandom(), and reads from /dev/random, all routed through the same JavaScript crypto.getRandomValues() — were disabled at Worker startup specifically so future snapshots would be possible. The interpreter itself, however, won’t bootstrap without an entropy call, and much of that entropy is used for two purposes: randomizing hash seeds and seeding pseudorandom generators.

Hash seeds get set at startup and stay fixed; Python offers no mechanism to rotate them later. PRNGs need a more deliberate approach:

  • At deploy time: Python starts with a fixed “poison seed,” and its PRNG state is recorded. Every API path into the PRNG is then wrapped so that any call from top-level user code fails the deployment. The snapshot is captured after top-level execution.
  • At run time: The restored isolate verifies the PRNG state was not touched — if it changed, an overlay was missed and deployment fails with an internal error. Then, before any handlers execute, the random generator is reseeded.

The net effect: Workers that never touch the PRNG during their top-level scope deploy normally, use real randomness at runtime, and never inherit identical random sequences from a shared base snapshot.

WebAssembly tables need replaying

Linear memory is only part of a Wasm instance’s state. Two tables live outside it, and both must match the captured state exactly:

  • The function pointer table resolves what are effectively code addresses in WebAssembly’s Harvard architecture — indexes into a table instead of jumps into a shared memory space.
  • A table of JavaScript objects referenced from Python, which cannot be represented as memory addresses and are instead stored as table indexes into the JS virtual machine.

Function pointers are set when the instance initializes and are appended by the dynamic loader when native libraries such as numpy load. Cloudflare handles that by patching the loader to record the load order, where library metadata lands in memory, and the relocation base for function pointers. Restores reverse the process: reloading libraries in the same order ensures metadata lands at identical addresses; assertions guard the table sizes match recorded bases.

JavaScript references get a smaller treatment. Any referenced JS object reachable from globalThis through a stable chain of property accesses is automatically replayed when the snapshot restores. Anything referenced that cannot be reached that way causes the deployment to fail. In practice this covers the top-level import paths used by all currently supported Python packages:

from js import fetch

Routing to reduce how often cold starts happen at all

Beyond fast startup, the platform makes fewer cold starts necessary by routing requests to already-warm isolates instead of spinning up new ones. Workers previously might prefer a fresh instance; that logic has shifted so requests preferentially hit existing running instances. The change shipped first on Python Workers — where cold starts are far more expensive than in JavaScript — and doubled as a practical testbed for the routing approach before broader rollout.

What comes next for Python Workers

The declared roadmap further to improved tooling, faster cold starts built on the isolate architecture, a broader package ecosystem footprint, and new capabilities including native TCP sockets, native WebSockets, and more bindings. Documentation lives in the Cloudflare developers site, and support conversations happen on the Cloudflare Discord.

Correction note: This post was updated with additional details regarding AWS Lambda.