Deploying to Vercel
The default deployment target. Vercel's Edge runtime handles middleware (CORS, request IDs, rate limiting); serverless functions handle everything else.
Import the repo
- Vercel dashboard → Add New → Project.
- Connect your Git provider, pick the Krios repo.
- Framework preset: Next.js (auto-detected).
- Build command: stays default (
pnpm build). Thepackage.jsonscript runsprisma generate && next build, so no extra config needed. - Output directory:
.next(default). - Install command:
pnpm install.
Environment variables
Set the variables documented in Environment variables under Settings → Environment Variables before the first deploy:
DATABASE_URL postgres://...?sslmode=require (Neon pooled)
NEXTAUTH_SECRET $(openssl rand -base64 32)
NEXTAUTH_URL https://cms.example.com
ADMIN_EMAIL admin@example.com
ADMIN_PASSWORD <one-time-setup>
SUPABASE_URL https://<proj>.supabase.co
SUPABASE_SERVICE_KEY <from supabase>
SUPABASE_STORAGE_BUCKET krios-media
CRON_SECRET $(openssl rand -base64 32)
Build
The first deploy runs prisma generate (always safe) and Next.js's production build. Vercel caches node_modules between deploys; the prisma directory triggers a regenerate when schema.prisma changes.
Cron jobs
vercel.json (committed in the repo) registers two handlers, both hourly (top of the hour):
{
"crons": [
{ "path": "/api/cron/scheduled-publish", "schedule": "0 * * * *" },
{ "path": "/api/cron/webhook-retry", "schedule": "0 * * * *" }
]
}
Vercel invokes each one hourly with Authorization: Bearer ${CRON_SECRET}. The handler verifies the secret and returns 401 otherwise. (You can subsample within the hour via CRON_SCHEDULED_PUBLISH_INTERVAL_MIN / CRON_WEBHOOK_RETRY_INTERVAL_MIN so Neon can scale to zero between ticks.)
/api/cron/cache-invalidation exists as a route but is not scheduled in vercel.json — it's invoked manually / in tests, and its tag purge is currently a logging stub.
Custom domain
Settings → Domains to add cms.example.com. Vercel issues a Let's Encrypt certificate automatically.
Update NEXTAUTH_URL to match — the admin UI uses it for absolute callbacks (preview deep-links, embed picker URLs).
Edge cache
Vercel's edge cache obeys the Cache-Control headers Krios sets:
Cache-Control: public, max-age=60, stale-while-revalidate=3600
Surrogate-Key: entry:{id} type:{apiName} site:{slug} project:{slug}
The /api/cron/cache-invalidation route is intended to purge tags by surrogate key when entries publish or unpublish, but it is not scheduled (no entry in vercel.json) and its purge is currently a logging stub rather than a live CDN call. It's invokable manually / in tests. If you're hosting elsewhere, wire this route to your CDN's tag-purge API.
Deploys
git push origin master # auto-deploys on the default Vercel hook
vercel --prod # explicit production deploy from the CLI
Each push gets a preview deployment; main-branch pushes go to production. Database migrations don't run on every deploy — see Production checklist for the safe migration sequence.