Edge & serverless deployment
PyWire has two deployment tiers. Stateful keeps page instances and a server/client push channel. Stateless keeps no page instance between events: every POST restores a signed snapshot, dispatches one handler, renders dirty regions, and returns a new snapshot.
Choose the tier by who owns the timeline:
- Use stateful for server-pushed work such as
{$await},push_state(), or per-token chat/streaming. - Use stateless when bounded request/response events are enough. Polling and optimistic UI make this practical without a persistent server timeline.
PyWire(stateless=True) is a build-time ceiling assertion. pywire build rejects page closures that use {$await} or a statically visible push_state() call. See Stateless mode and the tier feature matrix.
Deployment support matrix
Section titled “Deployment support matrix”| Host | Stateful | Stateless | Timeline owner / ideology |
|---|---|---|---|
| Docker | Yes | Yes | Stateful: server pushes; stateless: request-scoped |
| Fly.io | Yes | Yes | Stateful: server pushes; stateless: request-scoped |
| Render | Yes | Yes | Stateful: server pushes; stateless: request-scoped |
| Railway | Yes | Yes | Stateful: server pushes; stateless: request-scoped |
| Google Cloud Run | Yes | Yes | Stateful: server pushes; stateless: request-scoped |
| Cloudflare Durable Objects | Yes, at the edge | No | Regional Durable Objects run the app and push updates |
Cloudflare plain Workers (cloudflare-edge) |
No | Yes | Each request owns its event; the client carries the timeline |
| AWS Lambda | No | Yes | Request-scoped |
| Azure Functions | No | Yes | Request-scoped |
| GCP Cloud Functions | No | Yes | Request-scoped |
Container and long-running platforms can host either tier. FaaS/one-shot targets are stateless because they do not retain a page between invocations. The Cloudflare Durable Object target is the stateful edge alternative.
Measured reality
Section titled “Measured reality”In the real Cloudflare workerd + Pyodide gate, the counter action round-trip was p50 ≈ 2.65 ms, the counter snapshot was 232 B, a 1000-row keyed-list toggle was 251 B, and Pyodide cold start was ≈ 79 ms. These are measured-in-workerd observations, not service promises.
AWS Lambda, Azure Functions, and GCP Cloud Functions run native CPython, not Pyodide. Do not apply the Cloudflare cold-start framing to them.
Start stateless
Section titled “Start stateless”from pywire import PyWire
app = PyWire(stateless=True) # reads PYWIRE_SECRET_KEYA signing secret is mandatory. PyWire(stateless=True) refuses to start without secret_key= or PYWIRE_SECRET_KEY; PyWire never generates one implicitly.
For provider-specific commands and secret setup, see Provider quickstarts.