Skip to main content

Decision

New crons, queues, schedulers and heavy or long-running backend work belong on Cloudflare: Workers Cron Triggers, Queues, Workflows and Durable Objects. Do not add them as Vercel Cron entries, Next.js after() continuations, new /api/cron processor routes, pg_cron jobs, Supabase Edge Functions or Trigger.dev tasks. This applies to all new services and to every legacy owner as it migrates. Supabase remains the system of record. The rule moves compute and scheduling, not data: Workers keep using the existing Supabase tables, RPCs, claims and RLS through injected credentials. It does not authorize a database migration, a new datastore or a production cutover. Every Cloudflare job must meet the runaway-work and cost acceptance rules: bounded work and retries, idempotent progress, no rescheduling after a terminal state, and a tested durable stop fence. Usage alerts and CPU limits are not spending caps.

Choose the Cloudflare primitive

The background work inventory maps every current owner to its target and cutover class. Reuse Cron Control for recurring jobs instead of adding another controller for the same work. Preserve current cadence when moving a schedule; changing it needs job-specific evidence and a product decision.

Shrink-only guard

scripts/ci/cloudflare-backend-ownership.test.js runs in the root script suite and fails when a new owner appears outside Cloudflare: A migration after the rule’s cutoff (20261023000000) may not call cron.schedule, even for an allowlisted name, except to re-tune a justified retained Postgres job. A changed cadence is a new owner. Tests, paused runtimes and the guard’s own files are excluded. scripts/ci/cloudflare-backend-ownership-allowlist.js records each legacy owner with its owner, Cloudflare target and plan. The test pins the adopted baseline; entries may be removed but never added. Removing a legacy definition without removing its entry fails as stale, so the list shrinks with each migration.
To retire an entry: move the schedule or processor to Cloudflare behind its default-off flag, complete the cutover, delete the legacy definition in its own reviewed change, then delete the allowlist entry. Never run both owners unfenced.

Retained Postgres exceptions

pg_cron may keep a job only when it is pure in-database maintenance: a single SQL statement against Postgres tables with no HTTP, storage or provider side effects, where a Cloudflare trigger would add a Worker invocation and a service-key round trip to run the same SQL in the same database. Each retained job carries its justification in the allowlist; jobs using pg_net are not exceptions. New exceptions are not admitted through the guard: schedule new work from Cloudflare.

Regression evidence

The guard test covers the repository scan, allowlist metadata, the frozen baseline, new and re-cadenced Vercel crons, catalog enablement, stale entries, post-cutoff migrations, unschedule replay, SQL comments/quoting/PL/pgSQL parsing, Edge Function source and configuration, Trigger.dev configuration/dependencies/imports, Next after() imports and new cron routes. It does not certify hosted schedules, processor behavior or spend; those remain per-job cutover evidence.