PayloadSolutions

Deployment

Environment variables, migrations and hosting.

Payload Stack is a standard Next.js application with a database. It runs on Vercel, on any Node host, or in the included Docker image.

Environment variables

From .env.example:

VariableRequiredNotes
DATABASE_URLyesConnection string for the adapter you chose
PAYLOAD_SECRETyesPayload encryption and JWT secret
BETTER_AUTH_SECRETrecommendedFalls back to PAYLOAD_SECRET. 32+ random characters.
NEXT_PUBLIC_APP_URLyesPublic origin, used for auth callbacks and email links
RESEND_API_KEY, EMAIL_FROMfor emailWithout them, emails are logged, not sent
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, NEXT_PUBLIC_STRIPE_PRICE_*for billingSee Billing
<PROVIDER>_CLIENT_ID, <PROVIDER>_CLIENT_SECRETper social provider
BLOB_READ_WRITE_TOKEN, S3_*, R2_*, AZURE_STORAGE_*, GCS_*, UPLOADTHING_TOKENfor the storage adapter you choseWithout them uploads stay on local disk. See Media storage
CRON_SECRETfor scheduled actionsLets a clock run the job queue; without it that endpoint is admin-only. See Scheduled actions
RUN_JOBS_IN_PROCESSlong-lived hosts onlytrue runs the queue in this process. Never on serverless.

src/lib/env.ts validates them at boot and fails with the missing name.

Migrations

Generate a migration whenever collections change and run migrations during deploy:

pnpm db:migrate:create
pnpm db:migrate && pnpm build

Vercel

Import the repository, set the variables above, use a managed PostgreSQL (Vercel Postgres, Neon, Supabase) and set the build command to pnpm db:migrate && pnpm build. Vercel's filesystem is ephemeral, so configure a storage adapter (Vercel Blob is the shortest path) or uploads vanish on the next deploy. Add the Stripe webhook endpoint after the first deploy.

Nothing runs between requests on Vercel, so a project with scheduled actions needs a clock: Payload Clock is free and runs every minute on any plan, and a vercel.json cron is the alternative (once a day on Hobby). Either way CRON_SECRET has to be set in the Vercel project as well as locally.

Docker

The included Dockerfile builds a standalone Next.js image. Provide the environment variables at runtime and run migrations before starting the container. This is the one place RUN_JOBS_IN_PROCESS=true belongs, if the project has scheduled actions — on one instance.

Checklist before launch

  • An admin account exists (/admin bootstrap is disabled once one does).
  • auth.allowSignUp and auth.requireEmailVerification are set as intended.
  • Legal pages reviewed in the admin (they are seeded placeholders).
  • Stripe webhook endpoint created for production and the portal enabled.
  • NEXT_PUBLIC_APP_URL is the final domain (passkeys are bound to it).
  • Uploads go to a storage adapter, not local disk, unless the host has a persistent volume.
  • Something runs the job queue, if the project has scheduled actions — the admin's Run queue button proves the endpoint works; a clock proves it keeps working.

On this page