Deployment
cfstack ships to Cloudflare Workers, not Cloudflare Pages. The deployment contract is:
- tracked
wrangler.tomlis the template-safe baseline and local-development config - the tracked source-owned
custom-workerentrypoint is the canonical Worker runtime boundary scheduled()is owned in tracked source and continues through/api/cron- private
wrangler.remote.tomlholds preview and production tenant identity - committed
wrangler.remote.example.tomlis the copy source for the private file .env.cloudflare*is for remote helper, seeding, and Cloudflare API credentials.dev.vars.devis only for the local Wrangler lane
Lane summary
Keep the three runtime lanes separate:
- Local app:
npm run dev -- --port 3101 - Local worker:
npm run cf:preview - Local worker smoke:
npm run test:e2e:worker - Preview deploy:
npm run cf:deploy:preview - Production deploy:
npm run cf:deploy
The local app lane stays on port 3101. The local worker lane stays on Wrangler’s default 8787 port. The worker smoke lane is explicit and opt-in; it does not replace the default local-app Playwright lane.
Do not repurpose tracked wrangler.toml into a preview or production tenant file.
Prerequisites
Before deploying, make sure you have:
- A Cloudflare account
- Wrangler CLI installed
- Valid Wrangler auth or API credentials
- A private
wrangler.remote.toml
npx wrangler whoamiPrepare remote config
Bootstrap the untracked remote config:
cp wrangler.remote.example.toml wrangler.remote.tomlFill wrangler.remote.toml with your real:
account_id- preview and production routes/domains
- D1 database IDs
- KV namespace IDs
- R2 bucket names or mappings
Keep tracked wrangler.toml template-safe for local development and shared source defaults.
Prepare remote helper credentials
Copy the helper/API example for each remote lane you use:
cp .env.cloudflare.example .env.cloudflare
cp .env.cloudflare.example .env.cloudflare.previewUse those files for:
CLOUDFLARE_API_TOKENorCLOUDFLARE_D1_TOKEN- remote seeding values such as
APP_URLandXADMIN_SECRET - other helper-only API credentials
Do not use .env.cloudflare* to smuggle account IDs, routes, or binding IDs back into tracked source.
Local-only env files vs remote secrets
Use the right place for each class of configuration:
- Local Wrangler-only values:
.dev.vars.devfrom.dev.vars.example - Next.js local development values:
.env.local - Remote Worker secrets: Cloudflare secret storage or the repo helper scripts
- Remote tenant identity: private
wrangler.remote.toml - Remote helper/API credentials:
.env.cloudflare*
Upload Worker secrets with the repo scripts:
# Preview Worker secrets
npm run secrets:upload:preview
# Production Worker secrets
npm run secrets:upload:productionLocal verification before deploy
Use the right lane before you touch preview or production:
# local-app lane
npm run dev -- --port 3101
# default browser signoff for local-app
npm run test:e2e
# local-worker lane
npm run cf:preview
# explicit worker-runtime smoke
npm run test:e2e:workerUse npm run test:e2e:worker:headed when you need visible-browser debugging against the Worker lane on 8787.
Recommended release order
When the release includes application code only:
npm run lint
npm run typecheck
npm run cf:deploy
npm run test:smoke:prodWhen the release includes a D1 schema change:
npm run lint
npm run typecheck
npm run db:release:d1
npm run cf:deploy
npm run test:smoke:prodUse the preview lane the same way:
npm run db:release:d1:preview
npm run cf:deploy:preview
npm run test:smoke:previewThe ordering matters:
- Release D1 schema first when the Worker expects new indexes or columns
- Deploy the Worker second so runtime code matches the released schema
- Run read-only smoke checks last against the actual deployed URL
Command reference
Build the local-worker lane
npm run cf:build:devRun the local-worker lane
npm run cf:previewThis path should exercise the source-owned custom-worker entrypoint, including the tracked scheduled() boundary.
Run the local-worker smoke lane
npm run test:e2e:worker
npm run test:e2e:worker:headedBuild production Worker
npm run cf:buildBuild preview Worker
npm run cf:build:previewDeploy preview Worker
npm run cf:deploy:previewDeploy production Worker
npm run cf:deployPost-deploy smoke checks
The repo includes read-only Playwright smoke commands for the public routes:
npm run test:smoke:preview
npm run test:smoke:prodFor local runtime truth, keep the lanes separate:
npm run test:e2estays the default app-lane Playwright suite on3101.npm run test:e2e:workeris the explicit local Worker smoke suite on8787.
Current smoke coverage includes:
//login/pricing/docs/docs/getting-started/installation/AGENTSand/docs/AGENTSmust stay404
D1 release notes
Remote D1 release commands use scripts/release-d1.ts and verify remote indexes after the schema push:
npm run db:release:d1
npm run db:release:d1:previewUse those commands instead of ad-hoc drizzle-kit push when changing production or preview D1 schema.
CI/CD outline
For CI, keep the same release order as local operations and materialize the private remote config at job runtime:
- Install dependencies
- Write
wrangler.remote.tomlfrom CI secrets or another private source - Authenticate Wrangler with
CLOUDFLARE_API_TOKEN - Run
npm run lint - Run
npm run typecheck - Run
npm run db:release:d1if the release changes schema - Run
npm run cf:deploy - Run
npm run test:smoke:prod
That keeps local-app, local-worker, preview deploy, and production deploy aligned without mutating tracked wrangler.toml.