# Environment Variables

> Operator reference for every Seedly Sites environment variable - what each setting does, where it goes, and which ones are required.

Every operator-facing setting a live instance cares about, grouped by area. Three kinds of value:

- **generated** - the platform's own secrets, produced by `npx pnpm run gen:secrets`
- **provider** - a key you get from a third party (Railway, Cloudflare, Stripe, and so on)
- **config** - a plain value you choose

**Where they go:** app-owned secrets and most config go on the Railway **cms** service (saving redeploys it). A few also go on the **pagebuilder** service or as **GitHub Actions secrets** for the deploy pipeline. In the local sandbox most of these are unset and the platform falls back to safe defaults.

The authoritative check is the doctor: `npx pnpm run setup:check -- --target=prod` names exactly what is missing for a live instance. This page explains what each value is for - never the values themselves. Keep every secret in a password manager ([Backups](/docs/help/backups)) and never commit one to Git.

---

## Core / Auth

| Name | Kind | Required | Purpose |
|------|------|----------|---------|
| `PAYLOAD_SECRET` | generated | yes | Signs sessions and tokens. Distinct per environment; rotating it signs everyone out |
| `SUPER_ADMIN_EMAIL` | config | yes | The locked super-admin identity. Set your own; the doctor flags the vendor default |
| `CRON_SECRET` | generated | yes (prod) | Authenticates scheduled-job routes (backups, scheduled publish, retention) |
| `SEED_SECRET` | generated | no | Gates the one-time bootstrap endpoint |
| `DEPLOY_STATUS_SECRET` | generated | no | Authenticates deploy-status callbacks from the tenant deploy workflow |
| `PAYLOAD_EMAIL` / `PAYLOAD_PASSWORD` | config / generated | no | Service-account login the build and render tooling use to reach the CMS API |
| `INSTANCE_BRAND_NAME` | config | no | White-label name shown in admin chrome and emails |
| `SUPPORT_EMAIL` | config | no | Support contact surfaced to clients |

## Database

| Name | Kind | Required | Purpose |
|------|------|----------|---------|
| `PAYLOAD_ADAPTER` | config | yes (prod) | Unset = the local sandbox file database; set for the Postgres adapter in production |
| `DATABASE_URL` | provider | yes (prod) | The Railway Postgres connection string |

## Cloudflare (Media + Hosting)

| Name | Kind | Required | Purpose |
|------|------|----------|---------|
| `R2_ENDPOINT` | provider | yes (prod) | The R2 storage endpoint for durable media. Without the R2 keys, storage falls back to ephemeral local disk |
| `R2_ACCESS_KEY_ID` | provider | yes (prod) | R2 access key id |
| `R2_SECRET_ACCESS_KEY` | provider | yes (prod) | R2 secret access key |
| `R2_BUCKET_NAME` | config | no | Bucket name (has a sensible default) |
| `R2_PUBLIC_URL` | config | yes (build) | Public base URL media is served from; applied when a site is built |
| `CF_API_TOKEN` | provider | yes (prod) | Token the platform uses to auto-create per-site Pages projects. Also a GitHub Actions secret |
| `CF_ACCOUNT_ID` | config | yes (prod) | Your Cloudflare account id |

## GitHub Deploy

| Name | Kind | Required | Purpose |
|------|------|----------|---------|
| `GITHUB_DEPLOY_TOKEN` | provider | yes (prod) | A GitHub token (repo and workflow scope) that dispatches the tenant deploy workflow |
| `GITHUB_DEPLOY_REPO` | config | yes (prod) | The repo the deploy workflow runs in. Set your OWN repo; the doctor flags the vendor default |

## AI

| Name | Kind | Required | Purpose |
|------|------|----------|---------|
| `ANTHROPIC_API_KEY` | provider | yes | Claude API key for brand-DNA generation, port revision, and visual QA |
| `SEEDLY_DNA_MODEL` / `SEEDLY_QA_MODEL` / `SEEDLY_REVISE_MODEL` | config | no | Override the model each AI flow uses |

## Email

| Name | Kind | Required | Purpose |
|------|------|----------|---------|
| `SENDGRID_API_KEY` | provider | yes (prod) | Transport for auth and notification email. Unset = those emails are logged, not sent |
| `EMAIL_FROM_ADDRESS` | config | yes (prod) | Default From address; must be a verified sender at your email provider |

## Studio / Render Wiring

| Name | Kind | Required | Purpose |
|------|------|----------|---------|
| `STUDIO_INTERNAL_URL` | config | yes (prod) | The studio's internal URL; the CMS proxies the builder to it so the builder is same-origin in production |
| `CMS_BASE_URL` | config | no | Canonical CMS origin for absolute links |
| `RENDER_BASE_URL` | config | no | CMS-to-render base for preview links |
| `PAYLOAD_API_URL` | config | no | The CMS API origin the render/studio build talks to |
| `PUBLIC_CMS_URL` | config | no | The CMS origin baked into the studio build |
| `STUDIO_ORIGIN` | config | no | An extra allowed origin for a dev-direct studio |

## Billing (Optional)

All optional: leave unset and billing is simply inert. See [Billing](/docs/help/billing).

| Name | Kind | Purpose |
|------|------|---------|
| `STRIPE_SECRET_KEY` | provider | Your Stripe API key |
| `STRIPE_WEBHOOK_SECRET` | provider | Verifies Stripe webhook signatures |
| `STRIPE_PRICE_ID` | config | The subscription price the Subscribe button checks out |
| `BILLING_DEFAULT_ENFORCEMENT` | config | Instance default when a lapsed tenant's enforcement is set to "default": edit lock, suspend, or none |

## Optional Integrations (Fail Soft When Unset)

| Name | Purpose |
|------|---------|
| `GOOGLE_PLACES_API_KEY` | Places lookup for location capture |
| `GOOGLE_MAPS_EMBED_API_KEY` | Maps embed for the location card |
| `INDEXNOW_KEY` | IndexNow key; unset = the search-engine ping on deploy is skipped |
| `SENTRY_DSN` / `NEXT_PUBLIC_SENTRY_DSN` | Server and client error monitoring |
| `NEXT_PUBLIC_ADMIN_LOGO_URL` | White-label logo on the admin/login screen |
| `NEXT_PUBLIC_ENABLE_AB_TESTS` | Enables the experimental A/B tests feature |
| `SEEDLY_BACKUP_DIR` | Backup output directory |

> The intake (generate-from-brand-DNA) flow has additional keys of its own; they are intentionally not documented here yet.

---
Source: https://seedlysites.com/docs/help/env-vars
