SKILL.md
Vercel for SaaS
Core Insight: Optimize three axes: operational control (CLI-first), cost control (avoid accidental builds), correctness (caching + auth without breaking SSR).
Table of Contents
- [THE EXACT PROMPT](#the-exact-prompt)
- [Architecture Decision](#architecture-decision)
- [Quick Start](#quick-start-manual-deploy)
- [Stop Build Credit Burn](#stop-build-credit-burn)
- [Environment Variables](#environment-variables)
- [Workflow Checklist](#workflow-checklist)
- [Critical Gotchas](#critical-gotchas)
- [Core Commands](#core-commands)
- [AGENTS.md Blurb](#agentsmd-blurb)
- [References](#references)
THE EXACT PROMPT
Set up [Next.js app / SaaS] on Vercel with:
1. Cloudflare DNS at: [domain.com]
2. Supabase project: [project-ref]
3. Build control: [manual deploys only / CI-triggered / default]
4. Cost offload: [R2 for assets / Cloudflare Pages for marketing / none]
Follow vercel-for-saas skill patterns.
Architecture Decision
Where should traffic go?
│
├─ Authenticated app (SSR/RSC) ─────► Vercel (app.example.com)
│ • DNS-only in Cloudflare (no proxy)
│ • Manual deploys, prebuilt artifacts
│
├─ Marketing/docs (static) ─────────► Cloudflare Pages (www.example.com)
│ • Direct Upload for deploy control
│ • Aggressive caching, cheap bandwidth
│
└─ Uploads/assets (storage) ────────► Cloudflare R2 (assets.example.com)
• Worker-signed URLs
• No egress fees
Why this split: Vercel does NOT recommend reverse-proxying. Hostname splitting gets Cloudflare benefits without breaking Vercel's edge.
Quick Start: Manual Deploy
# 1. Pull settings + env (creates .vercel/)
vercel pull --yes --token $VERCEL_TOKEN
# 2. Build locally (skips Vercel build minutes)
vercel build --prod
# 3. Upload prebuilt artifact
vercel deploy --prebuilt --prod --token $VERCEL_TOKEN
This is the highest-leverage pattern for cost control.
Stop Build Credit Burn
| Method | How | Effectiveness |
|---|---|---|
| Disable auto-deploys | vercel.json: "git": { "deploymentEnabled": false } |
★★★★★ |
| Prebuilt workflow | Build outside Vercel, upload with --prebuilt |
★★★★☆ |
| Ignored Build Step | Exit 0 = skip, Exit 1 = build (commonly reversed!) | ★★☆☆☆ |
| Deploy Hooks | HTTP-triggered deploys only | ★★★☆☆ |
Config-as-code (recommended):
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"git": { "deploymentEnabled": false }
}
Details: [BUILD-CONTROL.md](references/BUILD-CONTROL.md)
Environment Variables
# List
vercel env ls
# Add secret (interactive)
vercel env add SECRET_NAME production
# Pull to local .env
vercel env pull .env.local
# Bulk sync via REST (for agents)
curl -X POST "https://api.vercel.com/v10/projects/$PROJECT/env?upsert=true" \
-H "Authorization: Bearer $VERCEL_TOKEN" \
-d '{"key":"VAR","value":"val","target":["production"]}'
For OIDC + Vault: [TOKEN-AND-KEY-MANAGEMENT.md](references/TOKEN-AND-KEY-MANAGEMENT.md)
Domain Setup
# Add domain to Vercel
vercel domains add example.com --token $VERCEL_TOKEN
# Then in Cloudflare DNS (DNS-only, not proxied):
# A record → Vercel IP OR
# CNAME → cname.vercel-dns.com
Workflow Checklist
Phase 1: Initial Setup
- Install CLIs:
vercel,supabase,wrangler,jq - Set tokens:
VERCELTOKEN,CLOUDFLAREAPI_TOKEN - Link project:
vercel link --yes - Pull env:
vercel pull --yes
Phase 2: Cost Optimization
- Add
"git": { "deploymentEnabled": false }to vercel.json - Set up prebuilt deploy workflow in CI
- Move marketing to Cloudflare Pages — [CLOUDFLARE-OFFLOAD.md](references/CLOUDFLARE-OFFLOAD.md)
- Move assets to R2 — [CLOUDFLARE-OFFLOAD.md](references/CLOUDFLARE-OFFLOAD.md)
Phase 3: Secrets & Auth
- Store secrets via
vercel env addor REST API - Configure OIDC for cloud credentials — [TOKEN-AND-KEY-MANAGEMENT.md](references/TOKEN-AND-KEY-MANAGEMENT.md)
- Set up Supabase auth integration
Phase 4: Database
- Create migrations:
supabase migration new - Apply:
supabase migration up(local) /supabase db push(CI) - Generate types:
supabase gen types typescript
Two-Button Deploy (Agent-Friendly)
# Marketing (Cloudflare Pages)
pnpm -C apps/marketing build
npx wrangler pages deploy apps/marketing/dist --branch=main
# App (Vercel prebuilt)
vercel pull --yes && vercel build --prod && vercel deploy --prebuilt --prod
Critical Gotchas
| Gotcha | Fix |
|---|---|
| Ignored Build Step exits reversed | 0 = skip, 1 = build (not intuitive) |
| Ignored builds still count toward quotas | Use git.deploymentEnabled: false instead |
| Cloudflare proxy + Vercel = problems | Use DNS-only for Vercel domains |
.vercel/ folder committed |
Add to .gitignore |
| Secrets in config files | Use vercel env or wrangler secret put |
Full list: [FOOTGUNS.md](references/FOOTGUNS.md)
Core Commands
| Task | Command |
|---|---|
| Deploy preview | vercel |
| Deploy prod | vercel --prod |
| Pull settings | vercel pull |
| Build locally | vercel build |
| Deploy prebuilt | vercel deploy --prebuilt |
| List env vars | vercel env ls |
| Add env var | vercel env add NAME target |
| Pull env to file | vercel env pull |
Full CLI reference: [CLI-COMMANDS.md](references/CLI-COMMANDS.md)
AGENTS.md Blurb
Copy this to your project's AGENTS.md:
### Vercel
Vercel CLI is installed and authenticated.
Project: `<PROJECT_NAME>`
Common commands:
\`\`\`bash
vercel pull --yes # Fetch settings + env
vercel build --prod # Build locally (saves build minutes)
vercel deploy --prebuilt --prod # Deploy prebuilt artifact
vercel env ls # List env vars
vercel env add SECRET production # Add secret
\`\`\`
**Cost tip:** Use prebuilt workflow to avoid burning build credits.
References
| Topic | Reference |
|---|---|
| Architecture & cost model | [ARCHITECTURE.md](references/ARCHITECTURE.md) |
| Stopping build burn | [BUILD-CONTROL.md](references/BUILD-CONTROL.md) |
| API-based build control | [API-BUILD-CONTROL.md](references/API-BUILD-CONTROL.md) |
| All CLI commands | [CLI-COMMANDS.md](references/CLI-COMMANDS.md) |
| Secrets, env, OIDC, Vault | [TOKEN-AND-KEY-MANAGEMENT.md](references/TOKEN-AND-KEY-MANAGEMENT.md) |
| R2 + Pages cost offload | [CLOUDFLARE-OFFLOAD.md](references/CLOUDFLARE-OFFLOAD.md) |
| Full automation blueprint | [AUTOMATION-BLUEPRINT.md](references/AUTOMATION-BLUEPRINT.md) |
| Common mistakes | [FOOTGUNS.md](references/FOOTGUNS.md) |
Token Setup (Non-Interactive Auth)
# Vercel: https://vercel.com/account/tokens
export VERCEL_TOKEN=xxx
# Cloudflare: https://dash.cloudflare.com/profile/api-tokens
export CLOUDFLARE_API_TOKEN=xxx
# Supabase: project settings → API
export SUPABASE_ACCESS_TOKEN=xxx
All CLIs read these automatically. No interactive login needed.