MainSDK
Search⌘K
How-to

Ship with deploy config

Generate Vercel, Cloudflare, Fly or Docker files from main.yaml, run schedules on a serverless platform, fail the build on a missing secret and review a staging to production change.

Building for 0.4

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

You will generate the platform's deploy files from main.yaml, wake a schedule through the tick route, and review what a promotion to production changes. It takes about 20 minutes. MainSDK writes and checks the files; the platform's Git integration or CLI still deploys.

You need

  • A MainSDK app with a main.yaml and a production overlay main.env.prod.yaml (see Deploy on Vercel).
  • The app's data regions declared in db.regions.

1. Pick a target and a schedule

Set the target in the production overlay and declare the schedule in main.yaml:

main.env.prod.yaml
deploy:
  target: vercel
  vercel: { plan: pro }
main.yaml
jobs:
  schedules:
    nightly-report: "0 3 * * *"

2. Write the files

bash
npx mainsdk deploy config --env prod

This writes vercel.json (regions, one cron for nightly-report, a build command that runs the preflight), deploy/env.prod.example and mainsdk.deploy.json. Commit all three. Keys you added to vercel.json yourself are not touched. Run it again after any change to main.yaml; mainsdk deploy config --check and mainsdk check fail when a generated block is stale.

3. Serve the tick route

On vercel and cloudflare the platform's cron calls one route. Mount it before your own routes and set CRON_SECRET in the platform:

app/api/mainsdk/route.ts
import { app } from "mainsdk";
import { createTick } from "mainsdk/deploy";

const tick = createTick(app());
export const POST = (req: Request) => tick.handle(req);

On Cloudflare Workers export scheduled: (event) => tick.scheduled(event) next to fetch, and the same job is queued in process with no HTTP call.

4. Let the build refuse a missing secret

The generated build command runs mainsdk deploy preflight --env prod first. Try it with a name unset:

bash
CRON_SECRET=x npx mainsdk deploy preflight --env prod

It exits 1 with MAIN-DEPLOY-0003 and lists the missing names, never a value.

5. Review the promotion

bash
npx mainsdk deploy diff --from staging --to prod --fail-on widen

It lists changed config, env names, schedules, regions and grants, and exits 1 with MAIN-DEPLOY-0012 for each change that lets production do more than staging.

Check it worked

Run npx mainsdk check --env prod. It passes when the generated files match main.yaml, the regions sit inside the data regions and the schedules fit the plan.

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet