Multi-Tenant Patterns
Billing by Plan
interface Env {
DISPATCHER: DispatchNamespace
CUSTOMERS_KV: KVNamespace
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const userWorkerName = new URL(request.url).hostname.split('.')[0]
const customerPlan = await env.CUSTOMERS_KV.get(userWorkerName)
const plans = {
enterprise: { cpuMs: 50, subRequests: 50 },
pro: { cpuMs: 20, subRequests: 20 },
free: { cpuMs: 10, subRequests: 5 },
}
const limits = plans[customerPlan as keyof typeof plans] || plans.free
const userWorker = env.DISPATCHER.get(userWorkerName, {}, { limits })
return await userWorker.fetch(request)
},
}
Resource Isolation
Complete isolation: Create unique resources per customer
- KV namespace per customer
- D1 database per customer
- R2 bucket per customer
const bindings = [
{
type: 'kv_namespace',
name: 'USER_KV',
namespace_id: `customer-${customerId}-kv`,
},
]
Hostname Routing
Wildcard Route (Recommended)
Configure */* route on SaaS domain → dispatch Worker
Benefits:
- Supports subdomains + custom vanity domains
- No per-route limits (regular Workers limited to 100 routes)
- Programmatic control
- Works with any DNS proxy settings
Setup:
- Cloudflare for SaaS custom hostnames
- Fallback origin (dummy
A 192.0.2.0if Worker is origin) - DNS CNAME to SaaS domain
*/*route → dispatch Worker- Routing logic in dispatch Worker
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const hostname = new URL(request.url).hostname
const hostnameData = await env.ROUTING_KV.get(`hostname:${hostname}`, { type: 'json' })
if (!hostnameData?.workerName) {
return new Response('Hostname not configured', { status: 404 })
}
const userWorker = env.DISPATCHER.get(hostnameData.workerName)
return await userWorker.fetch(request)
},
}
Subdomain-Only
- Wildcard DNS:
*.saas.com→ origin - Route:
*.saas.com/*→ dispatch Worker - Extract subdomain for routing
Orange-to-Orange (O2O) Behavior
When customers use Cloudflare and CNAME to your Workers domain:
| Scenario | Behavior | Route Pattern |
|---|---|---|
| Customer not on Cloudflare | Standard routing | */* or *.domain.com/* |
| Customer on Cloudflare (proxied CNAME) | Invokes Worker at edge | */* required |
| Customer on Cloudflare (DNS-only CNAME) | Standard routing | Any route works |
Recommendation: Always use */* wildcard for consistent O2O behavior.
Custom Metadata Routing
For Cloudflare for SaaS: Store worker name in custom hostname custom_metadata, retrieve in dispatch worker to route requests. Requires custom hostnames as subdomains of your domain.
Observability
Logpush
- Enable on dispatch Worker → captures all user Worker logs
- Filter by
OutcomeorScript Name
Tail Workers
- Real-time logs with custom formatting
- Receives HTTP status,
console.log(), exceptions, diagnostics
Analytics Engine
// Track violations
env.ANALYTICS.writeDataPoint({
indexes: [customerName],
blobs: ['cpu_limit_exceeded'],
})
GraphQL
query {
viewer {
accounts(filter: { accountTag: $accountId }) {
workersInvocationsAdaptive(filter: { dispatchNamespaceName: "production" }) {
sum {
requests
errors
cpuTime
}
}
}
}
}
Use Case Implementations
AI Code Execution
async function deployGeneratedCode(name: string, code: string) {
const file = new File([code], `${name}.mjs`, { type: 'application/javascript+module' })
await client.workersForPlatforms.dispatch.namespaces.scripts.update('production', name, {
account_id: accountId,
metadata: { main_module: `${name}.mjs`, tags: [name, 'ai-generated'] },
files: [file],
})
}
// Short limits for untrusted code
const userWorker = env.DISPATCHER.get(sessionId, {}, { limits: { cpuMs: 5, subRequests: 3 } })
VibeSDK: For AI-powered code generation + deployment platforms, see VibeSDK - handles AI generation, sandbox execution, live preview, and deployment.
Reference: AI Vibe Coding Platform Architecture
Edge Functions Platform
// Route: /customer-id/function-name
const [customerId, functionName] = new URL(request.url).pathname.split('/').filter(Boolean)
const workerName = `${customerId}-${functionName}`
const userWorker = env.DISPATCHER.get(workerName)
Website Builder
- Deploy static assets + Worker code
- See api.md for full implementation
- Salt hashes for asset isolation
Best Practices
Architecture
- One namespace per environment (production, staging)
- Platform logic in dispatch Worker (auth, rate limiting, validation)
- Isolation automatic (no shared cache, untrusted mode)
Routing
- Use
*/*wildcard routes - Store mappings in KV
- Handle missing Workers gracefully
Limits & Security
- Set custom limits by plan
- Track violations with Analytics Engine
- Use outbound Workers for egress control
- Sanitize responses
Tags
- Tag all Workers: customer ID, plan, environment
- Enable bulk operations
- Filter efficiently