Skip to Content
Getting StartedDeployment

Deployment

cfstack ships to Cloudflare Workers, not Cloudflare Pages. The deployment contract is:

  • tracked wrangler.toml is the template-safe baseline and local-development config
  • the tracked source-owned custom-worker entrypoint is the canonical Worker runtime boundary
  • scheduled() is owned in tracked source and continues through /api/cron
  • private wrangler.remote.toml holds preview and production tenant identity
  • committed wrangler.remote.example.toml is the copy source for the private file
  • .env.cloudflare* is for remote helper, seeding, and Cloudflare API credentials
  • .dev.vars.dev is 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:

npx wrangler whoami

Prepare remote config

Bootstrap the untracked remote config:

cp wrangler.remote.example.toml wrangler.remote.toml

Fill 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.preview

Use those files for:

  • CLOUDFLARE_API_TOKEN or CLOUDFLARE_D1_TOKEN
  • remote seeding values such as APP_URL and XADMIN_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.dev from .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:production

Local 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:worker

Use npm run test:e2e:worker:headed when you need visible-browser debugging against the Worker lane on 8787.

When the release includes application code only:

npm run lint npm run typecheck npm run cf:deploy npm run test:smoke:prod

When 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:prod

Use the preview lane the same way:

npm run db:release:d1:preview npm run cf:deploy:preview npm run test:smoke:preview

The 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:dev

Run the local-worker lane

npm run cf:preview

This 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:headed

Build production Worker

npm run cf:build

Build preview Worker

npm run cf:build:preview

Deploy preview Worker

npm run cf:deploy:preview

Deploy production Worker

npm run cf:deploy

Post-deploy smoke checks

The repo includes read-only Playwright smoke commands for the public routes:

npm run test:smoke:preview npm run test:smoke:prod

For local runtime truth, keep the lanes separate:

  • npm run test:e2e stays the default app-lane Playwright suite on 3101.
  • npm run test:e2e:worker is the explicit local Worker smoke suite on 8787.

Current smoke coverage includes:

  • /
  • /login
  • /pricing
  • /docs
  • /docs/getting-started/installation
  • /AGENTS and /docs/AGENTS must stay 404

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:preview

Use 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:

  1. Install dependencies
  2. Write wrangler.remote.toml from CI secrets or another private source
  3. Authenticate Wrangler with CLOUDFLARE_API_TOKEN
  4. Run npm run lint
  5. Run npm run typecheck
  6. Run npm run db:release:d1 if the release changes schema
  7. Run npm run cf:deploy
  8. Run npm run test:smoke:prod

That keeps local-app, local-worker, preview deploy, and production deploy aligned without mutating tracked wrangler.toml.

Last updated on