Skip to main content

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

  1. Vercel dashboard → Add New → Project.
  2. Connect your Git provider, pick the Krios repo.
  3. Framework preset: Next.js (auto-detected).
  4. Build command: stays default (pnpm build). The package.json script runs prisma generate && next build, so no extra config needed.
  5. Output directory: .next (default).
  6. 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.