Emdash CMS Cloudflare Free Tier Production Playbook
Deploy EmDash on Cloudflare Free plan with a reliable path, clear feature boundaries, and practical rollback guidance.
Executive summary
You can run EmDash in production on Cloudflare Free plan for core CMS workflows.
What you must accept: sandboxed plugin execution is unavailable, so worker_loaders must be removed from configuration.
This guide is optimized for stability first, not maximum feature surface.
Capability boundaries on Free plan
Available
- EmDash core site and admin flows
- D1-backed content data
- R2-backed media storage (within free usage)
- Workers deployment and routing
Not available
- Dynamic Workers for sandboxed plugins
- plugin marketplace-style untrusted runtime execution
If your launch depends on sandboxed third-party plugin execution, Free plan is not sufficient.
Pre-deployment checklist
Complete all checks before running deployment commands:
- Wrangler is authenticated (
wrangler loginalready completed) - project builds locally without cloud-only assumptions
wrangler.jsonchas D1 and R2 bindings aligned with runtime configworker_loadersentry is removed for Free plan- package manager choice is consistent (
npmorpnpm, no mixing)
Resource provisioning order
Provision resources in this exact order to reduce binding churn:
- Create D1 database
- Create or enable R2 and then create bucket
- Update
wrangler.jsoncwith exact names and IDs - Build and deploy Worker
Reason: D1 ID and bucket names become your source of truth for environment bindings.
# Provision D1 first
npx wrangler d1 create your-db-name
# Then provision R2 bucket
npx wrangler r2 bucket create your-media-bucket
Minimal wrangler.jsonc shape for Free plan
The following skeleton is intentionally small:
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "your-site-name",
"main": "./src/worker.ts",
"compatibility_date": "2026-04-08",
"compatibility_flags": ["nodejs_compat"],
"d1_databases": [
{
"binding": "DB",
"database_name": "your-db-name",
"database_id": "replace-with-d1-id"
}
],
"r2_buckets": [
{
"binding": "MEDIA",
"bucket_name": "your-media-bucket"
}
]
}
No worker_loaders section on Free plan.
Build and deploy flow
Run commands from your EmDash project root:
# choose one package manager and stick to it
npm run build
npm run deploy
If your project is pnpm-based:
pnpm build
pnpm deploy
Avoid cross-running npm and pnpm in the same deployment session unless lockfiles are intentionally synchronized.
Post-deploy validation checklist
Validate in this order:
- Frontend URL resolves and returns expected content
- Admin URL (
/_emdash/admin) is reachable - first admin setup completes successfully
- create and publish one test item
- upload one media file and confirm retrieval
A deployment is not complete until data write paths are verified, not just route reachability.
High-frequency failure modes and shortest fixes
Failure: deployment succeeds, admin fails to initialize
Likely cause: binding mismatch between runtime integration and wrangler.jsonc.
Fix path:
- verify binding names (
DB,MEDIA) are identical everywhere - redeploy after correcting config
- retry admin initialization
Failure: R2 command blocked by account prompt
Likely cause: R2 was not enabled in dashboard yet.
Fix path:
- enable R2 in Cloudflare dashboard
- accept billing terms (free usage still applies)
- recreate bucket command
Failure: plugin-related runtime error on Free plan
Likely cause: leftover sandbox configuration.
Fix path:
- remove
worker_loaders - disable sandbox-specific plugin setup
- redeploy and retest
Rollback strategy
Use a conservative rollback policy:
- keep last known good deploy commit tagged
- if production regression appears, redeploy previous tagged commit
- postpone schema or plugin changes until traffic stabilizes
Operationally, fast rollback beats deep hotfix during incident windows.
When to upgrade from Free to Paid
Upgrade only when one of these is true:
- you need sandboxed untrusted plugin execution
- your traffic or storage regularly exceeds free limits
- governance requires stronger plugin runtime isolation
Do not upgrade because of ambiguous UI wording in billing screens. Upgrade only for concrete capability requirements.