Cloudflare Workers
Deploy the dedicated Workers app with D1, R2, and Cloudflare Email Sending.
Deploy your Workers app with D1, R2, and Cloudflare email. Follow resource creation, configuration, preview, and deployment in order.
Before You Start
Have dependencies installed, a Cloudflare account with Workers/D1/R2/Email Sending access, and a domain for your deployment. Use apps/web-cloudflare; run commands from the repository root. Start locally with Quick Start if needed. Use a new remote database; existing products need an upgrade plan.
1. Prepare the Account and Resources
Install from the repo root and use the app’s Wrangler dependency; a global install is unnecessary:
pnpm install
pnpm --filter @musemvp/cloudflare exec wrangler login
pnpm --filter @musemvp/cloudflare exec wrangler d1 create <your-database-name>
pnpm --filter @musemvp/cloudflare exec wrangler r2 bucket create <your-bucket-name>Your account needs access to Workers, D1, R2, and Email Sending. Check current quotas, permissions, CPU, and bundle limits. The template does not guarantee a zero-cost deployment of every feature.
2. Configure Bindings
Edit apps/web-cloudflare/wrangler.jsonc. Replace demo resource IDs, domains, and names with your own.
{
"name": "your-app",
"main": ".open-next/worker.js",
"compatibility_date": "2026-09-30",
"compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
"workers_dev": false,
"preview_urls": false,
"routes": [{ "pattern": "app.example.com", "custom_domain": true }],
"assets": { "binding": "ASSETS", "directory": ".open-next/assets" },
"d1_databases": [{
"binding": "DB",
"database_name": "your-database-name",
"database_id": "<your-d1-id>",
"migrations_dir": "../../packages/db/migrations-d1"
}],
"r2_buckets": [{ "binding": "R2_BUCKET", "bucket_name": "your-bucket-name" }]
}This is a binding excerpt; keep other required template settings. DB and R2_BUCKET are case-sensitive. The databaseName in scripts/d1-migrations.ts also uses the demo database name. Update it to your D1 name before executing database commands.
3. Initialize Locally
pnpm muse:init --yes --app cloudflare
pnpm db:seed:d1
pnpm cf:typegen
pnpm dev:cfInit writes apps/web-cloudflare/.env.local and .dev.vars, generates auth/newsletter secrets, and applies local D1 migrations. It does not create remote D1 or R2. Local accounts default to [email protected] and [email protected], with MUSE_SEED_PASSWORD (default musemvp). These are development-only.
pnpm dev:cf runs Next development mode. Use pnpm cf:preview to check the actual Worker bundle and bindings. After editing .env.local, update .dev.vars too; the files do not continuously synchronize themselves.
4. Configure Production Mail, Auth, and Billing
Follow Environment Variables for Worker secrets. Set auth/newsletter signing secrets and Cloudflare mail credentials, then configure enabled OAuth, captcha, and payment gateways. Production uses MAIL_PROVIDER=cloudflare.
Set NEXT_PUBLIC_SITE_URL=https://app.example.com and other used NEXT_PUBLIC_* values at build time. Changing public variables only at Worker runtime does not update inlined client URLs.
Write individual secrets with app-scoped commands:
pnpm --filter @musemvp/cloudflare exec wrangler secret put BETTER_AUTH_SECRET
pnpm --filter @musemvp/cloudflare exec wrangler secret put NEWSLETTER_TOKEN_SECRET
pnpm --filter @musemvp/cloudflare exec wrangler secret put CLOUDFLARE_API_TOKENConfigure remaining variables on the platform or import a carefully filtered secrets file. Do not upload local seed and development settings wholesale.
5. Migrate and Deploy
pnpm db:migrate:d1:status
pnpm db:migrate:d1:remote
pnpm db:migrate:d1:status --strict
pnpm cf:deployThe remote migration script exports a backup, tests migrations on a local clone, and then applies them remotely. Protect backups containing user data. cf:deploy first runs a strict D1 state check and stops if migrations are pending; it does not automatically alter production data.
OpenNext uses a webpack build. pnpm build:cf builds the Next app; cf:preview / cf:deploy also build the Worker. Keep the lockfile and current OpenNext patch until a replacement version is verified. Regenerate types with pnpm cf:typegen after changing bindings instead of hand-editing cloudflare-env.d.ts.
Check Your Result
Run pnpm cf:preview and open its printed URL to check the built Worker and local bindings. After pnpm cf:deploy, open your configured HTTPS domain and check remote D1 state. Verify login using your production account flow, actual mail, and your own avatar upload/download. Do not create default development seed accounts remotely. Finish the Release Checklist.
Common Problems
- Check login/logout, OTP, and OAuth on the actual HTTPS domain.
- Avatar PUT/GET uses same-origin
/files/avatars/*into R2; check session, ownership prefix, and binding. - Use sandbox payments and confirm signed webhooks update contracts and entitlements.
D1 binding DB is required/R2 binding R2_BUCKET is required: fix bindings, notDATABASE_URLor S3 keys.- D1 rejects package interactive
transaction()calls. Use supportedbatch()for multi-statement writes; see Database Configuration.