Deployment (Vercel)
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.
For the documentation sites (Docusaurus on Cloudflare Pages), see Docs Deployment (Cloudflare).
Vercel project
| Setting | Value |
|---|---|
| Team | gdpr-labs (Hobby plan) |
| Project | ropa2-0 |
| Project ID | prj_AMQcMMleeyJH5HmSoBE8vvEePV5E |
| Team ID | team_FsNGjbnVJzdZrdiDKzRkojKU |
| Framework | Next.js (auto-detected, Turbopack) |
| Node.js | 22.x |
| Production domains | rat.gd, *.rat.gd |
| Repository | github.com/GDPR-Labs/ropa2.0 (private, org-owned) |
How deploys work: GitHub Actions, not Git integration
Vercel's native Git integration is not used. Since the repository moved into the GDPR-Labs GitHub organization (June 2026), the Git integration is unavailable: Vercel's Hobby plan does not support private repositories owned by a GitHub organization (Pro is required for that).
Instead, deploys run through the GitHub Actions workflow .github/workflows/vercel-deploy.yml:
| Trigger | Result |
|---|---|
Push to main | Production deployment (rat.gd, *.rat.gd repointed automatically) |
| Push to any other branch | Preview deployment (unique *.vercel.app URL) |
Push touching only non-app paths (docs/**, website/**, AddOns/**, tests, repo tooling — see paths-ignore in the workflow) | No app deploy — docs are handled by Cloudflare Pages, add-ons (e.g. the GForms Apps Script) are deployed separately |
The workflow runs the standard Vercel CI sequence:
vercel pull # fetch project settings + env vars for the target environment
vercel build # build on the GitHub runner (--prod on main)
vercel deploy --prebuilt # upload the build output, no remote build
Implementation notes:
VERCEL_ORG_IDandVERCEL_PROJECT_IDare hard-coded in the workflowenv:block — they are identifiers, not secrets, and pinning them avoids a per-clonevercel linkstep.- Authentication uses the
VERCEL_TOKENrepository secret (GitHub → repo → Settings → Secrets and variables → Actions). It is a Vercel personal token scoped to thegdpr-labsteam, created at vercel.com/account/settings/tokens. - The Vercel CLI is pinned to a major version (
vercel@54) in the workflow to avoid surprise breakage from@latest. - A
concurrencygroup cancels superseded in-flight deploys of the same branch. - Because the build runs with
vercel build, all environment variables are pulled from Vercel — nothing app-related is stored in GitHub secrets except the token.
Operational consequences
- Build logs live in GitHub Actions (repo → Actions → "Vercel Deploy"), not in the Vercel dashboard. The Vercel dashboard still shows the resulting deployments, runtime logs, and domains.
- If the
VERCEL_TOKENexpires or is revoked, deploys fail in Actions (red ✗ on the commit). Fix: create a new token and rungh secret set VERCEL_TOKEN --repo GDPR-Labs/ropa2.0. - There are no automatic PR comments with preview URLs (that was a Git-integration feature). The preview URL is printed at the end of the workflow's Deploy step.
- Rollback: in the Vercel dashboard, Deployments → … → Instant Rollback, or
vercel rollback/vercel promote <deployment-url>with the CLI.
If the team ever upgrades to Vercel Pro, the native Git integration can be restored (Project → Settings → Git → Connect
GDPR-Labs/ropa2.0) and the workflow deleted. The Vercel GitHub App is already installed on the GDPR-Labs organization.
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 (see table above)
- 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.
The GitHub Actions workflow pulls these automatically at build time via vercel pull — they are not duplicated as GitHub secrets.
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 the CI runner 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
The
rat.gdDNS zone is managed by Vercel DNS. This is why the dev docs site cannot sit behind Cloudflare Access on arat.gdsubdomain — see Docs Deployment (Cloudflare).
6. Deploying
Push to main. The GitHub Actions workflow (see above) builds and deploys to production; pushes to any other branch produce preview deployments.
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:
- The "Vercel Deploy" workflow run is green in GitHub Actions
- The new deployment shows READY / Production in the Vercel dashboard
-
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.