Skip to content

Getting started

Chris Taylor-Guest edited this page Oct 10, 2026 · 11 revisions

Getting started

Cactus Foundation is deployed to Vercel - a hosting platform that handles the technical side for you. If someone is setting up Cactus on your behalf, point them to the technical sections below. If you're setting it up yourself and comfortable with a terminal, read on.

Once your site is up and running, you don't need any of the information on this page for day-to-day use. Head to Managing pages, Appearance and design, or the Configuration reference instead.


Technical setup

The rest of this page is for developers and technical users setting up a new Cactus install.

Prerequisites

  • Node.js ≥ 22
  • A Vercel account (Hobby is fine for most features; Pro/Enterprise unlocks Vercel webhooks for automatic deployment status updates)
  • npm - this project uses npm only. Do not use yarn or pnpm.
  • A PostgreSQL database or a Neon API key to have one created automatically (see below)

Clone and install

git clone https://github.com/usersaynoso/cactus-foundation.git my-site
cd my-site
npm install

Environment variables

Copy .env.example to .env.local and fill in the values:

cp .env.example .env.local

Required - setup blocks without these

Variable Description
DATABASE_URL PostgreSQL pooled connection string. Can be provisioned automatically - see below.
SESSION_SECRET At least 32 random characters. Generate: openssl rand -base64 32
SITE_URL The canonical public domain, e.g. https://example.com. This is also the WebAuthn relying party ID and cannot be changed after the first passkey is registered.
VERCEL_API_TOKEN Vercel REST API token. Create at: Vercel dashboard → Account Settings → Tokens. Required for Edge Config writes, deployment status checks, and writing DATABASE_URL during automatic provisioning.
VERCEL_PROJECT_ID Vercel project ID. Find it at: Vercel dashboard → your project → Settings → General. Must be set and the project redeployed before setup can proceed.

Optional - feature is disabled until set

Variable Gates
NEON_API_KEY Automatic database provisioning during setup. Leave unset if you supply your own DATABASE_URL.
BREVO_API_KEY Email (password login, verification, recovery). Alternative: SMTP_HOST/SMTP_PORT/SMTP_USER/SMTP_PASS
CLOUDFLARE_WORKER_URL Media serving via Cloudflare Worker (required for all proxied providers; set automatically by the admin Deploy media Worker flow). CLOUDFLARE_WORKER_HOSTNAME is optional - derived from the URL when unset.
Backblaze B2 - B2_APPLICATION_KEY_ID, B2_APPLICATION_KEY, B2_BUCKET_NAME, B2_ENDPOINT Media uploads to B2
Cloudflare R2 - R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET_NAME Media uploads to R2
AWS S3 - S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_BUCKET_NAME, S3_REGION Media uploads to S3
DigitalOcean Spaces - SPACES_ACCESS_KEY_ID, SPACES_SECRET_ACCESS_KEY, SPACES_BUCKET_NAME, SPACES_REGION Media uploads to Spaces
Wasabi - WASABI_ACCESS_KEY_ID, WASABI_SECRET_ACCESS_KEY, WASABI_BUCKET_NAME, WASABI_REGION Media uploads to Wasabi
MinIO - MINIO_ENDPOINT, MINIO_ACCESS_KEY_ID, MINIO_SECRET_ACCESS_KEY, MINIO_BUCKET_NAME, MINIO_USE_SSL Media uploads to MinIO
Vercel Blob - BLOB_READ_WRITE_TOKEN Media uploads to Vercel Blob
Supabase Storage - SUPABASE_STORAGE_PROJECT_URL, SUPABASE_STORAGE_SERVICE_ROLE_KEY, SUPABASE_STORAGE_BUCKET_NAME Media uploads to Supabase Storage
Cloudinary - CLOUDINARY_CLOUD_NAME, CLOUDINARY_API_KEY, CLOUDINARY_API_SECRET Media uploads to Cloudinary (direct CDN, no Worker)
ImageKit - IMAGEKIT_PUBLIC_KEY, IMAGEKIT_PRIVATE_KEY, IMAGEKIT_URL_ENDPOINT Media uploads to ImageKit (direct CDN, no Worker)
GITHUB_API_TOKEN Module and theme install/update (needs repo scope)
EDGE_CONFIG, VERCEL_EDGE_CONFIG_ID Fast Edge Config reads
VERCEL_WEBHOOK_SECRET Automatic deploy status (Pro/Enterprise only)
TURNSTILE_SITE_KEY, TURNSTILE_SECRET_KEY Cloudflare Turnstile bot protection
CLOUDFLARE_ZONE_ID, CLOUDFLARE_PURGE_API_TOKEN Clearing cached pages the instant they are edited, when Settings → General → Speed → Keep ready-made copies of your pages is on. Both can be entered in the admin, on that same Speed tab. The page cache works fine without them - an edited page simply waits out its window. The token needs the Zone → Cache Purge permission.
SENTRY_DSN Error reporting
MEMBER_AREA_PATH Web address prefix for the member account area (see Members). Defaults to account. Deploy-time only - changing it needs a redeploy.
STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET Shop module - card payments via Stripe (see Shop). All three must be set before Stripe appears as a payment option at checkout.
PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET, PAYPAL_WEBHOOK_ID, PAYPAL_MODE Shop module - PayPal payments. PAYPAL_MODE is sandbox or live.

Local development

# Apply the database schema (creates a migration file and updates the database)
npm run db:migrate
# Equivalent to: prisma migrate dev

# Start the dev server
npm run dev

Open http://localhost:3000. If the database is empty (no SiteConfig row), you'll be redirected to /_setup to run the setup wizard.

When Cactus runs off Vercel it switches into local-development mode (detected by the absence of Vercel's VERCEL=1 variable). The setup wizard skips the Vercel connect step and reads config from .env.local; the VERCEL_API_TOKEN/VERCEL_PROJECT_ID variables can be left unset, and you set DATABASE_URL, SESSION_SECRET, and SITE_URL/NEXT_PUBLIC_SITE_URL=http://localhost:3000 instead. Some admin features (the env editor, core/module updates, the redeploy buttons) are read-only or hidden locally. See Running locally for the full picture.

First deploy to Vercel

  1. Push your repo to GitHub.
  2. Import the project in the Vercel dashboard.
  3. Add all required environment variables in Vercel's project settings (SESSION_SECRET, SITE_URL, NEXT_PUBLIC_SITE_URL, VERCEL_API_TOKEN, VERCEL_PROJECT_ID). You do not need to set DATABASE_URL yet if you plan to use automatic provisioning.
  4. Deploy. The build runs prisma migrate deploy automatically - but if DATABASE_URL isn't set yet, the build will fail at that step. If you're using automatic provisioning, let setup handle the database creation first (see below), then the next redeploy will succeed.
  5. Visit your production URL - you'll be redirected to /_setup.

The setup wizard

The wizard runs once, at /_setup, and completes in three steps:

  1. Connect your project - confirms required variables are set and your Vercel project is linked. If DATABASE_URL is absent, you will be offered one of the three paths below.
  2. Database - provisions or connects your PostgreSQL database and runs the schema migration.
  3. Account & configure - enter a username and email, then either register a passkey (fingerprint, Face ID, or security key) or, via the "Use Token instead of Passkeys" link, set up an authenticator app (TOTP) instead - both are equally valid ways to sign in. Then choose your secret admin path, site name, and timezone.

When setup completes, the wizard marks setupCompleted = true and redirects you to the admin dashboard.

Choosing a domain

Step 1 lists every domain already attached to the chosen Vercel project, plus any other domain sitting in your Vercel account waiting to be attached, and lets you add a brand new one. Whichever you pick becomes SITE_URL, so the wizard will not let you continue until that domain actually works.

Two separate things can be outstanding, and Vercel treats them independently:

  • The domain is not pointing at Vercel yet. You need the A (or CNAME) record the wizard shows you.
  • Vercel needs proof the domain is yours. This happens when the domain is also registered under a different Vercel account. Vercel issues a verification record - usually a TXT on _vercel.yourdomain.com - and will not serve the domain until it sees it.

A domain can need one, the other, or both. The wizard lists every record that is still outstanding in a copy-and-paste table, rechecks every ten seconds, and unlocks the Configure project button as soon as Vercel is happy. If you would rather not touch individual records, pointing the domain's nameservers at Vercel (ns1.vercel-dns.com, ns2.vercel-dns.com) satisfies both conditions on its own.

Until then you cannot continue, and that is deliberate rather than us being difficult. The domain you choose becomes the address your passkeys are bound to, and that binding cannot be changed once the first passkey exists - so a domain that never comes to life would strand the whole site. Better a short wait now than a locked front door later.

DNS changes can take a while to spread. If you would rather not sit and watch, pick your *.vercel.app address instead: it works the moment the project exists, needs none of the above, and lets you finish setup straight away. You can attach the proper domain afterwards - just be aware that switching the site's address later means re-registering passkeys.

Optional features (email, image storage, bot protection, etc.) are not configured during setup. After setup, the dashboard shows an Additional features checklist with friendly descriptions and direct links to the relevant settings section.

Database setup: three paths

Path 1 - DATABASE_URL already set (bring your own)

If DATABASE_URL is present in your environment, the wizard proceeds normally. No change from the standard flow.

Path 2 - Automatic provisioning via Neon (recommended for new installs)

If DATABASE_URL is absent and NEON_API_KEY is present, the wizard offers three options:

Option A - Create a fresh database automatically (recommended)

  1. Cactus calls the Neon API to create a new free-tier Neon Postgres project in your chosen region.
  2. The pooled connection string is written as DATABASE_URL to your Vercel project's environment variables via the Vercel REST API.
  3. Writing that env var triggers a Vercel redeployment. During that build, prisma migrate deploy runs automatically and applies the database schema. The wizard cannot continue in the same page load - this is unavoidable.
  4. The wizard shows a "redeploying" state and polls /api/health every 5 seconds. Once the database is reachable, it advances you automatically.

Idempotency: if the page refreshes or the deployment fails mid-flight, the wizard detects the existing Neon project by name and reuses it. The project name follows the pattern cactus-{VERCEL_PROJECT_ID}.

Option B - Use an existing Neon project

  1. The wizard lists all Neon projects in your account. Select the one you want to connect.
  2. Before connecting, Cactus checks whether the selected project already has database tables. If it does, you'll see a warning with two choices:
    • Use existing data - Cactus connects the project as-is. Any existing admin settings (site name, admin path, timezone) are pre-filled in the setup wizard. Prisma applies only pending migrations, leaving existing rows intact.
    • Destroy all existing data - Cactus drops and recreates the public schema before connecting. This gives Prisma a clean slate and is the right choice if you want a fresh Cactus install in a project that previously held other data.
  3. Cactus fetches the pooled connection URI, (optionally drops the schema), writes it to Vercel, then redeploys.

Warning: "Destroy all existing data" is irreversible - it permanently deletes everything in the database's public schema. Only choose this if you are certain you no longer need the existing data.

Option C - Supply your own DATABASE_URL

Paste any PostgreSQL connection string. Cactus writes it to Vercel and triggers a redeploy.

Why does the page pause? Vercel applies new environment variables only at redeploy time. The redeploy is the mechanism by which the build picks up the new variable and runs migrations. This is by design - the architecture guarantees migrations only ever run in the build step, never from the live app.

To use Path 2:

  1. Create a free Neon account at console.neon.tech.
  2. Go to Account Settings → API keys and generate a key.
  3. Add NEON_API_KEY to your Vercel project's environment variables and redeploy (or add it before the initial deploy).
  4. Ensure VERCEL_API_TOKEN and VERCEL_PROJECT_ID are also set - they are required for Cactus to write DATABASE_URL back to your project.
  5. Visit /_setup. The wizard will offer the three options above.

Path 3 - Manual (no Neon API key)

If DATABASE_URL is absent and NEON_API_KEY is also absent, the wizard shows manual instructions:

  1. Create a Postgres database anywhere (Neon, Supabase, self-hosted, etc.).
  2. Copy the pooled connection string.
  3. Add it as DATABASE_URL in your Vercel project's environment variables.
  4. Trigger a redeploy. Migrations run during the build automatically.
  5. Return to /_setup - setup continues from where it left off.

The wizard also shows a hint that setting NEON_API_KEY would allow automatic provisioning.

After setup

The site defaults to comingSoon status. To go live:

  1. Go to Settings → Site Status and set it to Live.
  2. Add optional credentials (email, media, GitHub, etc.) via the Additional features checklist on the dashboard, or navigate directly to Settings and the relevant tab.

Wiki: Home · Getting started · Managing pages · Appearance and design · Managing users · Members · Managing media · Modules · Configuration reference · Architecture overview · Authoring a module · Self-hosting and operations

Clone this wiki locally