Deployment
Deploy Prood applications to Vercel with Neon Postgres, Upstash Redis, and per-app configuration.
Prood deploys as separate Vercel projects sharing a single Neon Postgres database. Each app has its own environment variables and build configuration.
Production URL map (Prood)
| URL | App |
|---|---|
prood.com | Marketing (apps/web) |
dashboard.prood.com | Merchant admin |
api.prood.com | Commerce API |
checkout.prood.com | Hosted checkout |
docs.prood.com | Documentation |
admin.prood.com | Platform super-admin |
{slug}.prood.app | Storefront (wildcard on storefront project) |
shop.example.com | Store custom domain (per merchant) |
Set NEXT_PUBLIC_PLATFORM_DOMAIN=prood.app for storefront subdomain resolution only. Platform services use *.prood.com via each app's BETTER_AUTH_URL and public URL env vars.
Deployment architecture
Drag to pan · Scroll to zoom
Prerequisites
- Vercel account with team/project access
- Neon Postgres provisioned (Vercel marketplace integration)
- Upstash Redis provisioned (Vercel marketplace integration)
- Payment provider accounts (Stripe, Easypay, and/or Ifthenpay)
- Domain names configured
Step 1 — Database setup
Apply the versioned migrations to the production database (and again on every deploy that adds migrations; already-applied ones are skipped):
# Set DATABASE_URL to the production Neon connection string
DATABASE_URL="postgresql://..." pnpm db:migrateThis creates the commerce and Better Auth tables, the prood_app role and the
row-level-security policies. The role that runs the migrations must be able to
create roles (the Neon owner role can) and must be the role the apps connect
with, because 0000_app_role grants prood_app to it. Postgres 16 or later is
required. If prood_app already exists under another owner, a superuser must
first run GRANT prood_app TO <migrating_role> WITH ADMIN OPTION. Only run
pnpm db:seed if you want the demo store.
Step 2 — Deploy the API
The API must be deployed first — other apps depend on it.
| Setting | Value |
|---|---|
| Framework | Next.js |
| Root directory | apps/api |
| Build command | cd ../.. && pnpm build --filter=api |
| Node.js version | 24 |
Environment variables
DATABASE_URL=postgresql://...
BETTER_AUTH_SECRET=<openssl rand -base64 32>
AUTH_PROXY_SECRET=<openssl rand -base64 32>
BETTER_AUTH_URL=https://api.prood.com
AUTH_COOKIE_DOMAIN=.prood.com
KV_REST_API_URL=https://...
KV_REST_API_TOKEN=...
NEXT_PUBLIC_DASHBOARD_URL=https://dashboard.prood.com
NEXT_PUBLIC_DOCS_URL=https://docs.prood.com
API_PUBLIC_URL=https://api.prood.com
S3_ENDPOINT=https://<account>.eu.r2.cloudflarestorage.com
S3_BUCKET=prood-public
S3_ACCESS_KEY_ID=...
S3_SECRET_ACCESS_KEY=...
S3_PUBLIC_URL=https://prood-media.rbarona.workers.dev
# Also set S3_PUBLIC_URL on storefront, dashboard, admin and checkout: next/image
# reads it at build time and rejects the host without it.
CHECKOUT_API_SECRET=<shared secret>
INTEGRATION_ENCRYPTION_KEY=<openssl rand -base64 32>
RESEND_API_KEY=re_...
RESEND_FROM_EMAIL="Prood <noreply@prood.com>"
NEXT_PUBLIC_PLATFORM_DOMAIN=prood.appStep 3 — Deploy the storefront
| Setting | Value |
|---|---|
| Root directory | apps/storefront |
| Build command | cd ../.. && pnpm build --filter=storefront |
Environment variables
DATABASE_URL=postgresql://...
COMMERCE_API_URL=https://api.prood.com/v1
BETTER_AUTH_SECRET=<same as API>
KV_REST_API_URL=<same as API>
KV_REST_API_TOKEN=<same as API>
CHECKOUT_URL=https://checkout.prood.com
CHECKOUT_API_SECRET=<same as API>
RESEND_API_KEY=re_...
RESEND_FROM_EMAIL="Prood <noreply@casalume.example>"
NEXT_PUBLIC_PLATFORM_DOMAIN=prood.appCustom domains are added per-merchant via the dashboard Domains page.
Step 4 — Deploy the checkout app
| Setting | Value |
|---|---|
| Root directory | apps/checkout |
| Build command | cd ../.. && pnpm build --filter=checkout |
Environment variables
KV_REST_API_URL=https://...
KV_REST_API_TOKEN=...
CHECKOUT_API_SECRET=<same as API>
CHECKOUT_URL=https://checkout.prood.com
COMMERCE_API_URL=https://api.prood.com/v1
STOREFRONT_URL=https://storefront.example.comPayment credentials aren't environment variables. Each store enters its own under Integrations in the dashboard, and there is no platform fallback.
Configure payment webhooks to: https://checkout.prood.com/api/webhooks/{provider}/{orgId}
Step 5 — Deploy the dashboard
| Setting | Value |
|---|---|
| Root directory | apps/dashboard |
| Build command | cd ../.. && pnpm build --filter=dashboard |
Environment variables
DATABASE_URL=postgresql://...
COMMERCE_API_URL=https://api.prood.com/v1
BETTER_AUTH_SECRET=<same as API>
AUTH_PROXY_SECRET=<same as API>
NEXT_PUBLIC_AUTH_URL=https://api.prood.com
INTEGRATION_ENCRYPTION_KEY=<same as API>
VERCEL_TOKEN=<for domain provisioning>
STOREFRONT_VERCEL_PROJECT_ID=<storefront project ID>
VERCEL_TEAM_ID=<team ID>
NEXT_PUBLIC_PLATFORM_DOMAIN=prood.appDashboard auth (sign-in, sign-up, organization ops) is issued by apps/api but proxied through the dashboard at /api/auth/* so session cookies stay on the dashboard origin. This is required on separate *.vercel.app preview URLs; with custom domains, set AUTH_COOKIE_DOMAIN=.prood.com on the API project so cookies are also shared across subdomains.
Step 6 — Deploy the platform admin
| Setting | Value |
|---|---|
| Root directory | apps/admin |
| Build command | cd ../.. && pnpm build --filter=admin |
Environment variables
DATABASE_URL=postgresql://...
BETTER_AUTH_SECRET=<same as API>
AUTH_PROXY_SECRET=<same as API>
BETTER_AUTH_URL=https://api.prood.com
NEXT_PUBLIC_AUTH_URL=https://api.prood.com
NEXT_PUBLIC_ADMIN_URL=https://admin.prood.comapps/admin is for platform operations only. Keep it on a restricted domain. Access is limited to users with the admin role.
Step 7 — Deploy docs and web
| App | Root directory | Notes |
|---|---|---|
docs | apps/docs | Runs sync:openapi at build time |
web | apps/web | Static marketing site |
# Docs
NEXT_PUBLIC_DOCS_URL=https://docs.prood.com
# Web
NEXT_PUBLIC_WEB_URL=https://prood.com
NEXT_PUBLIC_DOCS_URL=https://docs.prood.com
NEXT_PUBLIC_DASHBOARD_URL=https://dashboard.prood.com
NEXT_PUBLIC_API_URL=https://api.prood.comShared secrets
These must be identical across projects:
| Secret | Apps |
|---|---|
BETTER_AUTH_SECRET | API, dashboard (SSR session validation), storefront |
AUTH_PROXY_SECRET | API, dashboard, admin |
CHECKOUT_API_SECRET | API, storefront, checkout |
INTEGRATION_ENCRYPTION_KEY | API, dashboard |
DATABASE_URL | API, storefront, dashboard, admin |
KV_REST_API_URL, KV_REST_API_TOKEN | API, storefront, checkout |
Caching in production
The storefront uses Next.js Cache Components:
- Catalog pages:
'use cache'with per-tenant tags - Cart/checkout/account: dynamic (cookie/session dependent)
- Cache invalidation: dashboard product updates trigger
revalidateTagvia API
Production checklist
- Strong secrets generated for
BETTER_AUTH_SECRET,AUTH_PROXY_SECRET,CHECKOUT_API_SECRET,INTEGRATION_ENCRYPTION_KEY NEXT_PUBLIC_PLATFORM_DOMAINconfigured- Upstash Redis provisioned for checkout and auth rate limits, with
KV_REST_API_URLandKV_REST_API_TOKENset on API, storefront and checkout - Payment webhooks pointed to checkout domain
- Resend sender uses a verified production domain
pnpm db:migraterun against production database- Custom domains verified for at least one merchant
- SSL certificates active on all domains