ShipNext

Environment Variables

Configure site URLs, auth, database, payments, storage, email, and support chat with .env.local.

ShipNext keeps sensitive credentials and deployment-specific URLs in environment variables, while product behavior switches live in config/website.ts. Before launch:

  1. Copy .env.example to .env.local. Do not commit .env.local.
  2. Complete the minimum values from Quick Launch.
  3. Add OAuth, Stripe, R2/S3, Resend, and Crisp credentials as needed.
cp .env.example .env.local

Restart pnpm dev after changing environment variables.

Files

FilePurpose
.env.exampleTemplate with supported variables and example values
.env.localLocal or deployment secrets; never commit it

Variables prefixed with NEXT_PUBLIC_ are bundled for the browser and must not contain secrets. All other variables are server-only.

App and auth

For local development, URL variables must match the dev server port.

VariableRequiredDescription
NEXT_PUBLIC_APP_URLYesPublic app URL with protocol. Used for Checkout, Portal, and redirects.
BETTER_AUTH_URLYesBetter Auth application root URL. Keep it equal to NEXT_PUBLIC_APP_URL.
BETTER_AUTH_SECRETYesSession and token signing secret. Regenerate for production.
NEXT_PUBLIC_APP_URL='http://localhost:3000'
BETTER_AUTH_URL='http://localhost:3000'
BETTER_AUTH_SECRET='<your-better-auth-secret>'

Generate the secret with:

openssl rand -base64 32

OAuth callback URLs:

  • Google: {BETTER_AUTH_URL}/api/auth/callback/google
  • GitHub: {BETTER_AUTH_URL}/api/auth/callback/github
VariableRequiredDescription
GITHUB_CLIENT_IDOptionalGitHub OAuth App client ID
GITHUB_CLIENT_SECRETOptionalGitHub OAuth App client secret
GOOGLE_CLIENT_IDOptionalGoogle Cloud OAuth client ID
GOOGLE_CLIENT_SECRETOptionalGoogle Cloud OAuth client secret

Database

VariableRequiredDescription
DATABASE_URLYesDatabase connection string. SQLite default: file:./app.db.
DATABASE_URL=file:./app.db

Initialize locally:

pnpm db:push

For PostgreSQL, use a postgresql://... URL and ensure the Drizzle adapter/provider matches the selected schema.

Payments - Stripe

Public Price IDs are read by pricing and Checkout:

VariableDescription
NEXT_PUBLIC_STARTER_MONTHLY_PRICE_IDStarter monthly Price ID
NEXT_PUBLIC_STARTER_YEARLY_PRICE_IDStarter yearly Price ID
NEXT_PUBLIC_PRO_MONTHLY_PRICE_IDPro monthly Price ID
NEXT_PUBLIC_PRO_YEARLY_PRICE_IDPro yearly Price ID

Server secrets:

VariableRequiredDescription
STRIPE_SECRET_KEYWhen using paymentsStripe secret key
STRIPE_WEBHOOK_SECRETWhen using webhooksStripe webhook signing secret

Webhook endpoint:

https://<your-domain>/api/payment/webhook

For local testing, forward with Stripe CLI:

stripe listen --forward-to localhost:3000/api/payment/webhook

Object storage - R2 / S3-compatible

VariableRequiredDescription
S3_ENDPOINTFor R2/S3-compatible providersAPI endpoint
S3_REGIONOptionalRegion, defaults to auto
S3_ACCESS_KEY_IDYesAccess key ID
S3_SECRET_ACCESS_KEYYesSecret access key
S3_BUCKET_NAMEYesBucket name
S3_PUBLIC_URLOptionalPublic object URL root
NEXT_PUBLIC_STORAGE_PUBLIC_URLOptionalBrowser-visible public URL root

URL resolution priority is:

  1. S3_PUBLIC_URL / NEXT_PUBLIC_STORAGE_PUBLIC_URL
  2. S3_ENDPOINT plus bucket
  3. AWS virtual-hosted style URL

Email - Resend

VariableRequiredDescription
RESEND_API_KEYWhen sending emailResend API key
MAIL_FROM_EMAILReservedPresent for platform convention; current sender is websiteConfig.email.fromEmail

Configure email.fromEmail in config/website.ts with an address from a verified Resend domain.

Support chat - Crisp

VariableRequiredDescription
NEXT_PUBLIC_CRISP_WEBSITE_IDOptionalCrisp Website ID. Empty means no chat widget.

The chat component lazy-loads the SDK and syncs logged-in user email/name when available.

Full local template

NEXT_PUBLIC_APP_URL='http://localhost:3000'

BETTER_AUTH_URL='http://localhost:3000'
BETTER_AUTH_SECRET=''

DATABASE_URL=file:./app.db

GITHUB_CLIENT_ID=''
GITHUB_CLIENT_SECRET=''

GOOGLE_CLIENT_ID=''
GOOGLE_CLIENT_SECRET=''

NEXT_PUBLIC_TURNSTILE_SITE_KEY=''
TURNSTILE_SECRET_KEY=''
NEXT_PUBLIC_GOOGLE_RECAPTCHA_SITE_KEY=''
GOOGLE_RECAPTCHA_SECRET_KEY=''
NEXT_PUBLIC_HCAPTCHA_SITE_KEY=''
HCAPTCHA_SECRET_KEY=''

NEXT_PUBLIC_STARTER_MONTHLY_PRICE_ID=''
NEXT_PUBLIC_STARTER_YEARLY_PRICE_ID=''
NEXT_PUBLIC_PRO_MONTHLY_PRICE_ID=''
NEXT_PUBLIC_PRO_YEARLY_PRICE_ID=''
STRIPE_SECRET_KEY=''
STRIPE_WEBHOOK_SECRET=''
PAYMENT_TESTING_ENABLED=true

S3_ENDPOINT=''
S3_REGION='auto'
S3_ACCESS_KEY_ID=''
S3_BUCKET_NAME=''
S3_PUBLIC_URL=''
S3_SECRET_ACCESS_KEY=''
NEXT_PUBLIC_STORAGE_PUBLIC_URL=''

RESEND_API_KEY=''
MAIL_FROM_EMAIL=''

NEXT_PUBLIC_CRISP_WEBSITE_ID=''

Launch checklist

  • NEXT_PUBLIC_APP_URL and BETTER_AUTH_URL use the production HTTPS domain.
  • BETTER_AUTH_SECRET is regenerated and not an example value.
  • DATABASE_URL points to production and migrations have run.
  • OAuth callback URLs are updated in Google/GitHub.
  • Stripe live keys and webhook secret are configured.
  • R2/S3 credentials and public URL are configured.
  • Resend domain is verified and email.fromEmail is valid.
  • Secrets are not committed to Git.

On this page