Deploying to Vercel
npx flare deployruns the Vercel CLI (vercel deploy --prod) from the app’s own
node_modules, so there is nothing to install globally and every project
pins its own version. Anything you add is forwarded straight through:
npx flare deploy --yes # don't asknpx flare deploy --scope my-team # a team other than your defaultnpx vercel deploy # a preview instead of productionvercel on its own works exactly as well — flare deploy is a shortcut, not
a wrapper, and it deliberately does nothing the CLI doesn’t. This is the
opposite of the Cloudflare deploy, which sequences
migrations, secrets and zone rules itself, because on Vercel every one of
those belongs to the platform.
The first deploy
Section titled “The first deploy”npx vercel link # connect this directory to a Vercel projectnpx flare deployVercel builds on its own machines, running the build script from
package.json:
"build": "prisma generate && next build"prisma generate has to be there. The generated client is written into
node_modules, which is not committed and not cached between builds, so a
build without it fails on the first import.
Environment variables
Section titled “Environment variables”Set these on the Vercel project (Settings → Environment Variables), not in a committed file:
| Variable | |
|---|---|
DATABASE_URL |
Postgres, pooled. Neon’s pooled string, not the direct one |
BETTER_AUTH_SECRET |
32+ random bytes. Generate a new one — never reuse your laptop’s |
BETTER_AUTH_URL |
The production URL, e.g. https://shop.example.com |
R2_ENDPOINT, R2_BUCKET, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY |
File uploads |
UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN |
Rate limits shared across instances |
RESEND_API_KEY, MAIL_FROM |
Email. Without them it prints to the log instead of sending |
.env.example in your app lists the same set, which is what to copy from.
Migrations are yours to run
Section titled “Migrations are yours to run”Nothing in the deploy touches your database. That is deliberate: Vercel can build the same commit several times over, in parallel, and a migration that runs from a build step runs an unpredictable number of times.
Point DATABASE_URL at production and apply them:
DATABASE_URL="postgres://…" npx flare migratewhich runs prisma migrate deploy — it applies what is already written in
prisma/migrations/ and never invents anything, so it is safe to run
repeatedly and safe to run against a database that matters. Writing a new
migration is prisma migrate dev, on your own machine, against your own
database.
Run migrations before the deploy when they add something the new code needs, and after when they drop something the old code still uses. That ordering is the whole of zero-downtime schema change, and no tool can decide it for you.
The first time: a checklist
Section titled “The first time: a checklist”A new Postgres database is empty, and nothing in the build fills it. So the
deploy goes green, you open the app, and sign-up answers 500 — because the
users table does not exist yet. Vercel’s logs show the status and not the
cause, which makes this a slow one to work out.
Once, from your own machine:
-
Get the connection string under the name Flare reads. Vercel’s Postgres integration sets
POSTGRES_PRISMA_URL(pooled) andPOSTGRES_URL_NON_POOLING(direct). Flare and Prisma readDATABASE_URL, so a local.envholding only the Vercel names fails withPrismaConfigEnvError: Cannot resolve environment variable: DATABASE_URL. Pass it for the one command rather than editing.env, so there is nothing to remember to undo:Terminal window DATABASE_URL="<your POSTGRES_PRISMA_URL>" npx flare migrate -
Apply with
migrate deploy, which is whatflare migrateruns. Do not reach forprisma migrate devwhen that fails: it wants a shadow database it can create and drop, which a managed Postgres will not permit, so it errors on permissions. It is also the wrong command here — its job is to write a migration, against your own database.If
prisma/migrations/is empty there is nothing to apply. Write the first one locally withnpx flare gen migration init, commit it, and run the above.npx prisma db pushis the shortcut: it makes the database match the schema with no migration history, which is fine for a prototype and a problem the first time two people need the same change. -
Seed, if the app needs rows to be usable.
Terminal window DATABASE_URL="<your POSTGRES_PRISMA_URL>" npx flare seedSeeds are not idempotent unless you wrote them that way — running this twice inserts twice.
-
Check
.envstill points at your own database, if you edited it rather than passing the variable inline.
After that the schema travels with the code: commit the migration, deploy, and
run flare migrate against production in the order described above.
Files go to R2, not Blob
Section titled “Files go to R2, not Blob”The storage helpers speak S3, pointed at a Cloudflare R2 bucket, on both
stacks. R2 charges nothing for bandwidth out, and an app that serves files is
mostly bandwidth out — what it costs has the arithmetic.
Vercel Blob works if you would rather keep everything in one account; you
would reimplement lib/storage.ts against its SDK.
A cold database is not a cold function
Section titled “A cold database is not a cold function”Neon’s free tier suspends a database that has been idle for five minutes, and the first query after that waits for it to wake. It reads like a slow function and isn’t: it is the plan, and the fix is a paid tier with no scale-to-zero, not anything in your code.
What doesn’t come with this stack
Section titled “What doesn’t come with this stack”Realtime. realtimeChannel().publish() delivers nothing here, because
Cloudflare’s half of it is a Durable Object per channel and Vercel has no
equivalent. It warns once per channel rather than failing quietly, and the write
that triggered it still succeeds. The
observability page links to Vercel’s own dashboard rather than charting your
traffic in the app. Choosing a stack is the honest list.