LogoMkSaaS Docs

Deploy to Cloudflare

Step-by-step checklist for shipping the MkSaaS template to Cloudflare Workers, D1, and R2.

Overview

This project compiles the Next.js 15 App Router app into a Cloudflare Worker using @opennextjs/cloudflare. The Worker entry (.open-next/worker.js) and static assets (.open-next/assets) are deployed by Wrangler with the configuration in wrangler.jsonc. Database access uses Cloudflare D1 (src/db/index.ts), and optional file storage is S3-compatible via src/storage/provider/s3.ts.

Use this guide as an end-to-end deployment checklist. Each section maps to a concrete action you must complete before running pnpm deploy.

Prerequisites

  • Cloudflare account with Workers, D1, and (optionally) R2 enabled.
  • Cloudflare API token with Account.Workers Scripts=Edit and Account.D1=Edit scopes; optional R2=Edit.
  • Node.js 20+, pnpm, and the project cloned locally.
  • Stripe, Resend, OAuth, Turnstile, AI provider credentials (see env.example).

Prepare the local environment

  1. Install dependencies and generate docs assets:
    pnpm install
    pnpm content
  2. Copy environment templates:
    cp env.example .env
    cp dev.vars.example .dev.vars
  3. Fill .env with local values (set CLOUDFLARE_* only if you plan to run migrations locally).
  4. (Optional) For local Worker-style dev, run pnpm cf-dev. The initOpenNextCloudflareForDev hook in next.config.ts reads .dev.vars.

Configure Cloudflare resources

1. Worker metadata

  • Update name, routes (if using a custom domain), and d1_databases[0].database_id within wrangler.jsonc.
  • Confirm the compatibility_date and nodejs_compat flag match the Workers runtime you target.

2. D1 database provisioning

  1. Create a D1 database (wrangler d1 create <name> or via dashboard) and note the database_id.
  2. Export the following so CLI tools can reach the database:
    export CLOUDFLARE_ACCOUNT_ID=<your-account-id>
    export CLOUDFLARE_D1_DATABASE_ID=<database-id>
    export CLOUDFLARE_API_TOKEN=<api-token-with-d1-access>
  3. Generate and apply schema migrations:
    pnpm db:generate      # optional, regenerates SQL from schema if you changed it
    pnpm db:migrate       # runs migrations locally via d1-http driver
    wrangler d1 migrations apply invisible-text --local
    wrangler d1 migrations apply invisible-text
    Replace invisible-text with your actual database name if you adopt a different naming convention.

If you plan to serve user-uploaded assets:

  1. Create an R2 bucket and generate an S3-compatible access key.
  2. Populate STORAGE_REGION, STORAGE_BUCKET_NAME, STORAGE_ACCESS_KEY_ID, STORAGE_SECRET_ACCESS_KEY, STORAGE_ENDPOINT, and STORAGE_PUBLIC_URL as secrets (see next section). The endpoint should include the bucket domain expected by s3mini (https://<account>.r2.cloudflarestorage.com or your custom domain).
  3. If you prefer using an R2 binding instead of S3-compatible keys, add it under r2_buckets in wrangler.jsonc and adapt src/storage/provider/s3.ts.

4. Environment variables & secrets

Split variables between Wrangler vars (non-secret) and secrets (sensitive):

  • Non-sensitive config can be set in wrangler.jsonc under "vars".
  • Secrets (API keys, OAuth secrets, BETTER_AUTH_SECRET, etc.) must be stored with Wrangler:
    wrangler secret put BETTER_AUTH_SECRET
    wrangler secret put STRIPE_SECRET_KEY
    wrangler secret put RESEND_API_KEY
    # ...repeat for all keys listed in cloudflare-env.d.ts
    Reference cloudflare-env.d.ts or env.example for the full list.

5. Scheduled jobs & integrations

  • If you use the automated credit distributor (src/app/api/distribute-credits/route.ts), store CRON_JOBS_USERNAME and CRON_JOBS_PASSWORD as secrets and add a Cloudflare Cron Trigger hitting that route (Workers → Triggers).
  • Configure Webhooks (Stripe, Resend, Feishu, Discord) to point at your deployed domain once it is live.

Build and test locally

  1. Verify code quality and build output:
    pnpm lint
    pnpm build
  2. Generate the Cloudflare Worker bundle and preview locally:
    pnpm preview        # Builds via opennextjs-cloudflare and runs a local preview worker
  3. Inspect .open-next/worker.js and .open-next/assets if you need to debug bundling issues.

Deploy

  1. Deploy to Cloudflare:
    pnpm deploy
    This runs opennextjs-cloudflare build followed by wrangler deploy.
  2. If you only want to push updated static assets or zipped worker code without activation, you can run:
    pnpm upload
  3. Configure DNS or routes:
    • For a Workers domain, rely on <worker-name>.<account-id>.workers.dev.
    • For a custom domain, add a route in wrangler.jsonc and create the required DNS record in Cloudflare.

Post-deployment checklist

  • Tail logs and ensure startup is clean:
    wrangler tail invisible-text    # swap with your worker name if you rename the worker
  • Trigger a sample request (homepage, dashboard) and confirm D1 queries succeed.
  • Run scripts against production if needed (pnpm list-users, etc.)—they use the same D1 binding via getCloudflareContext.
  • Validate Stripe, Resend, OAuth, Turnstile, and any AI provider endpoints with production keys.
  • Document which secrets you set and who owns them to simplify key rotation.

Troubleshooting tips

  • Worker fails with DB not configured: confirm d1_databases entry in wrangler.jsonc and that the Worker was deployed with the binding.
  • Requests error while fetching files: double-check S3/R2 credentials and the bucket endpoint format expected by s3mini.
  • Missing environment variables: cross-reference cloudflare-env.d.ts to ensure every required key is configured as a secret or var.
  • Migrations blocked: check that the API token includes Account.D1 permissions and that CLOUDFLARE_ACCOUNT_ID/CLOUDFLARE_D1_DATABASE_ID are exported in your shell session.