Deploy with Cloudflare Containers
Cloudflare Containers are generally available and can run Doso's existing Linux/amd64 Rust image. A Worker provides TLS, routing, and lifecycle control; the API container connects to managed Postgres over TLS.
Recommended architecture#
Start with one API instance selected by a stable ID. Cloudflare currently requires application-defined container routing; built-in autoscaling is not available. Random routing is unsafe for Doso until run ownership, settings invalidation, and event delivery are distributed.
Prerequisites#
- Workers Paid plan with Containers enabled.
- Node.js and Wrangler.
- Docker capable of building
linux/amd64. - Managed Postgres reachable from Cloudflare with TLS.
- A stable 32-byte settings encryption key.
- A Node-compatible home for
web: either its own Cloudflare Container or an external Next.js origin.
Worker package#
This folder contains a deployable Worker:
wrangler.jsoncdeclares the API Container, Durable Object binding, one-instance ceiling, and observability.src/index.tsforwards/v1/**to the stabledoso-api-primarycontainer and proxies other traffic toWEB_ORIGIN.package.jsonprovides development, type-check, and deploy scripts.
Install and configure from this folder:
1npm install2npx wrangler secret put DATABASE_URL3npx wrangler secret put DOSO_SETTINGS_KEY_HEX4npx wrangler secret put BETTER_AUTH_SECRET5npm run deployThe API Dockerfile needs the repository root as its build context. Build and
push the image first, then replace YOUR_ACCOUNT_ID in wrangler.jsonc:
1docker build --platform linux/amd64 \2 -f ../../../crates/doso-api/Dockerfile \3 -t registry.cloudflare.com/YOUR_ACCOUNT_ID/doso-api:latest ../../..4npx wrangler containers push \5 registry.cloudflare.com/YOUR_ACCOUNT_ID/doso-api:latestEnvironment and secrets#
Set public values in wrangler.jsonc and secrets with Wrangler:
1DATABASE_URL=postgresql://.../doso?sslmode=require2DOSO_SETTINGS_KEY_HEX=<64 hex characters>3DOSO_AUTH_MODE=jwks4DOSO_JWKS_URL=https://app.example.com/api/auth/jwks5DOSO_AUTH_ISSUER=https://app.example.com6DOSO_AUTH_AUDIENCE=https://app.example.com7DOSO_PUBLIC_API_URL=https://app.example.com8DOSO_POSTGRES_MAX_CONNECTIONS=39DOSO_HOSTED_RUNTIME=trueThe Container class passes these values into the process on each start. Keep
general internet egress enabled: Cloudflare outbound handlers only intercept
HTTP/HTTPS, so enableInternet=false also blocks direct Postgres on port 5432.
Restrict database credentials and require TLS at the database instead.
Scaling and lifecycle#
The example uses:
max_instances: 1;- stable container ID
doso-api-primary; - port
8080; sleepAfter: "30m";- request/response streaming without buffering.
An SSE connection can remain open, and the client reconnects if a rollout or sleep/wake cycle interrupts it. Reconnecting cannot recover process-local events that were never persisted, so the UI should reconcile status from Postgres after reconnect.
Before adding replicas, implement:
- run-owner leases and heartbeats instead of global startup stale-run cleanup;
- durable queues for ingestion and workflow work that outlives a request;
- shared pub/sub or database-backed event cursors;
- cross-process settings invalidation;
- a one-shot migration/release job.
Cloudflare databases and storage#
Cloudflare offers several data products, but they are not interchangeable:
- D1 is Cloudflare's serverless SQLite database, accessed through a Worker binding. The Axum/SQLx container cannot open a D1 connection. Using D1 for Doso's primary graph store requires a new store implementation, transaction model, migration path, and likely per-tenant sharding.
- Hyperdrive pools and accelerates connections from Workers to external Postgres/MySQL. Its connection string exists inside a Worker binding; it is not a Postgres endpoint the container can consume.
- R2 is appropriate for uploads, immutable artifacts, exports, and backups. It is not a replacement for Postgres or the settings encryption key. A future object-store abstraction can use its S3-compatible API.
- Durable Object SQLite is useful for routing or coordination state owned by the Worker. It does not replace Doso's relational store.
A narrow future D1 candidate is the tenant registry/audit control plane. Keep the primary graph and workflow data in Postgres.
Web and SaaS#
For the simplest first deployment, leave web on a Node host and set
WEB_ORIGIN to its HTTPS URL. To keep all compute on Cloudflare, deploy
web/Dockerfile as a second deterministic Container and route non-/v1
traffic to it; use external Postgres for Better Auth.
Multi-org is ../../saas, not a third container on this page.
Verify and operate#
- Confirm the API image reports healthy on port 8080.
- Test JWKS retrieval and an authenticated
/v1/**request. - Test upload limits and SSE through the Worker without buffering.
- Let the container sleep, then verify wake-up and settings decryption.
- Deploy a new image while streaming and verify reconnect/reconciliation.
- Inspect Worker, Durable Object, and Container logs separately.
Official references: Container scaling, environment variables, outbound traffic, D1, and Hyperdrive.