Deployment Guide
ropa2.0 is deployed as a Next.js application on Vercel with MongoDB Atlas as the database. Tenants are identified by subdomains (<shortName>.rat.gd). Each subdomain must resolve to the same Vercel deployment.
Prerequisites
- Node.js ≥ 20
- A MongoDB Atlas cluster (M10 or higher recommended for production)
- A Clerk application (Business plan for multi-org support)
- A Cloudflare account with Turnstile enabled
- A Vercel project connected to this repository
- A domain with wildcard DNS support
1. MongoDB
1.1 Create a cluster
Use MongoDB Atlas. Recommended: M10 (dedicated) for production; M0 (free shared) is fine for staging.
1.2 Create a database user
Database Access → Add New Database User — grant readWriteAnyDatabase on the target database.
1.3 Allowlist Vercel IPs
Network Access → Add IP Address → Allow Access from Anywhere (0.0.0.0/0).
Vercel serverless functions use dynamic egress IPs that cannot be statically allowlisted.
1.4 Get the connection string
Connect → Drivers → Node.js — copy the connection string:
mongodb+srv://<user>:<password>@<cluster>.mongodb.net/<dbname>?retryWrites=true&w=majority
Replace <dbname> with your target database name (e.g. ropa2).
2. Clerk
2.1 Create a Clerk application
- Sign in at https://clerk.com.
- Create a new application — enable Email and your required social providers.
- Switch to Organizations mode: Settings → Organizations → Enable Organizations.
2.2 Create the demo organization
The demo tenant is special — its clerkOrganizationId is hard-coded in src/lib/mongoose/organizationFromTemplateCreate.js (KNOWN_CLERK_ORG_IDS). Create a Clerk organization with slug demo and copy its ID into that constant before the first deploy.
2.3 Configure the webhook
Webhooks → Add Endpoint:
- URL:
https://<your-domain>/api/webhooks/clerk - Events to subscribe:
organization.updated,organization.deleted
Copy the Signing Secret — this becomes CLERK_WEBHOOK_SIGNING_SECRET.
2.4 Collect keys
From API Keys:
| Variable | Where in Clerk |
|---|---|
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Publishable key |
CLERK_SECRET_KEY | Secret key |
CLERK_WEBHOOK_SIGNING_SECRET | Webhook signing secret (step 2.3) |
3. Cloudflare Turnstile
- Sign in at https://dash.cloudflare.com.
- Turnstile → Add site — domain:
*.rat.gd(wildcard covers all tenants). - Copy the Site Key and Secret Key.
| Variable | Value |
|---|---|
NEXT_PUBLIC_TURNSTILE_SITE_KEY | Site key |
TURNSTILE_SECRET_KEY | Secret key |
4. Environment Variables
Set all of the following in Vercel → Settings → Environment Variables. Mark variables starting with NEXT_PUBLIC_ as available to all environments; server-only variables should be Production + Preview only.
Required
| Variable | Description |
|---|---|
MONGODB_URI | Full MongoDB Atlas connection string |
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY | Clerk frontend key |
CLERK_SECRET_KEY | Clerk backend key |
CLERK_WEBHOOK_SIGNING_SECRET | Svix signature secret for webhook verification |
NEXT_PUBLIC_SITE_DOMAIN | Root domain without protocol (e.g. rat.gd) |
NEXT_PUBLIC_TURNSTILE_SITE_KEY | Cloudflare Turnstile site key |
TURNSTILE_SECRET_KEY | Cloudflare Turnstile server-side secret |
Optional — logging & telemetry
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL | debug (dev), info (prod) | Pino log level |
NEXT_PUBLIC_SENTRY_DSN | — | Sentry DSN for browser error tracking |
SENTRY_DSN | — | Sentry DSN for server error tracking |
SENTRY_ORG | — | Sentry org slug (for source map upload at build time) |
SENTRY_PROJECT | — | Sentry project slug |
SENTRY_AUTH_TOKEN | — | Sentry auth token for source map upload |
AXIOM_TOKEN | — | Axiom ingest token (activates Pino → Axiom transport) |
AXIOM_DATASET | — | Axiom dataset name (e.g. ropa2-production) |
See telemetry.md for full Sentry + Axiom setup.
Optional — build
| Variable | Default | Description |
|---|---|---|
ANALYZE | false | Set to true to generate bundle analysis report |
CI | — | Set by Vercel automatically; suppresses Sentry build logs when absent |
5. DNS
All tenant subdomains route to the same Vercel deployment. Two DNS records are needed:
| Type | Name | Value |
|---|---|---|
A / CNAME | @ | Vercel IP / cname.vercel-dns.com |
CNAME | * | cname.vercel-dns.com |
The wildcard record catches all <shortName>.rat.gd subdomains. In Vercel → Project Settings → Domains, add:
rat.gd*.rat.gd
6. Vercel Deployment
6.1 Connect the repository
New Project → Import Git Repository — select gdpr-labs/ropa2.0.
6.2 Build settings
Vercel auto-detects Next.js. No changes needed.
6.3 Deploy
Push to main (or your production branch). Vercel builds and deploys automatically.
On the first successful deploy, run the demo data seed (step 7).
7. Seed Demo Data
The demo tenant (demo.rat.gd) is created via the admin route:
POST /api/admin/recreate-demo
This is a protected endpoint. Trigger it once after the first deploy to create the demo organization, its ROPAs, and link it to the Clerk org created in step 2.2.
8. Post-Deployment Verification
Check each item after deploying:
-
https://demo.<your-domain>loads without error - Clerk sign-in works and redirects back to the subdomain
- Cloudflare Turnstile challenge appears on protected forms
- Creating a new ROPA activity saves to MongoDB
- PDF export generates and downloads correctly
- Clerk webhook fires on org update (check server logs)
- Sentry receives a test error (see
telemetry.md#verification) - Axiom receives structured Pino logs (see
telemetry.md#axiom)
9. Development Setup
# Install dependencies
npm install
# Copy env files
cp .env.e2e.example .env.e2e # fill in E2E_CLERK_USER_EMAIL
# Create .env.local with the variables from section 4
# at minimum: MONGODB_URI, CLERK keys, TURNSTILE keys, NEXT_PUBLIC_SITE_DOMAIN
# Run dev server
npm run dev
Local subdomains: demo.localhost:3000, upf.localhost:3000.
For local subdomain routing to work, add entries to /etc/hosts:
127.0.0.1 demo.localhost
127.0.0.1 upf.localhost
Useful scripts
| Command | Description |
|---|---|
npm run dev | Start dev server |
npm run build | Production build |
npm test | Unit + service tests (Vitest) |
npm run test:e2e | Playwright E2E tests |
npm run lint | ESLint |
npm run analyze | Bundle size analysis (ANALYZE=true) |
10. Multi-Environment Strategy
| Environment | Branch | Domain | Notes |
|---|---|---|---|
| Production | main | *.rat.gd | Live data; all env vars required |
| Preview | Feature branches | Vercel preview URLs | Use staging Atlas cluster; Clerk test mode |
| Development | Local | *.localhost:3000 | .env.local; mongodb-memory-server optional |
Clerk has separate Development and Production instances — use the development instance keys for local and preview environments.