Respondeo
Guides

Deployment

Deploy Respondeo to production

Deployment

This guide covers deploying Respondeo to various environments.

Prerequisites

  • Node.js runtime installed
  • A PostgreSQL database (or SQLite for simple deployments)
  • An OIDC provider for authentication (see authentication.md)

Environment Variables

Copy .env.example to .env.local (development) or .env (production) and configure all required variables:

cp .env.example .env.local

Required Variables

VariableDescription
NEXT_PUBLIC_APP_URLPublic URL where the app is hosted
BETTER_AUTH_SECRETSecret key for session encryption (min 32 chars)
BETTER_AUTH_URLURL for the auth service (usually same as app URL)
OIDC_CLIENT_IDOAuth client ID from your identity provider
OIDC_CLIENT_SECRETOAuth client secret
OIDC_ISSUEROIDC issuer URL (e.g., https://auth.example.com)
OIDC_PROVIDER_IDUnique identifier for your OIDC provider (e.g., "my-idp")
NEXT_PUBLIC_OIDC_PROVIDER_IDPublic provider ID (must match OIDC_PROVIDER_ID)

Optional Variables

VariableDefaultDescription
DB_DIALECTsqliteDatabase type: sqlite or postgres
DATABASE_URLquiz.dbDatabase connection string
RBAC_*See belowRole-based access control settings

See database.md for database setup and rbac.md for access control configuration.

Local Development

# Install dependencies
pnpm install

# Set up database
pnpm db:push

# Start development server
pnpm dev

The app will be available at http://localhost:3000.

Production Build

# Build the application
pnpm build

# Start production server
pnpm start

Docker Deployment

Using Docker Compose

The repository includes a compose.yaml for easy deployment with PostgreSQL:

# Start all services
docker compose up -d

# View logs
docker compose logs -f

# Stop services
docker compose down

Building the Docker Image

# Build the image
docker build -t quiz-app .

# Run with environment variables
docker run -d \
  -p 3000:3000 \
  -e NEXT_PUBLIC_APP_URL=https://quiz.example.com \
  -e BETTER_AUTH_SECRET=your-secret-key \
  -e BETTER_AUTH_URL=https://quiz.example.com \
  -e OIDC_CLIENT_ID=your-client-id \
  -e OIDC_CLIENT_SECRET=your-client-secret \
  -e OIDC_ISSUER=https://auth.example.com \
  -e DB_DIALECT=postgres \
  -e DATABASE_URL=postgresql://user:pass@db:5432/quiz \
  quiz-app

Vercel Deployment

The app is optimized for Vercel deployment:

  1. Push your repository to GitHub
  2. Import the project in Vercel Dashboard
  3. Configure environment variables in Vercel's project settings
  4. Deploy

Vercel Environment Variables

Set all required environment variables in Settings → Environment Variables:

  • NEXT_PUBLIC_APP_URL - Your Vercel deployment URL
  • BETTER_AUTH_SECRET - Generate with openssl rand -base64 32
  • BETTER_AUTH_URL - Same as NEXT_PUBLIC_APP_URL
  • OIDC_* - Your OIDC provider credentials
  • DATABASE_URL - PostgreSQL connection string (use Vercel Postgres or external)

Database Options for Vercel

  1. Vercel Postgres - Managed PostgreSQL, integrates automatically
  2. Neon - Serverless Postgres with generous free tier
  3. Supabase - PostgreSQL with additional features
  4. PlanetScale - MySQL-compatible (requires schema changes)

Co-locate functions with your database. apps/web/vercel.json pins regions: ["iad1"] (Washington, D.C.) to match a Neon database in AWS us-east-1. If you host the database elsewhere, change this — otherwise every query on every request pays the round trip between the two regions.

Rate limiting on Vercel

Configure REDIS_URL (or VALKEY_URL). Vercel runs many concurrent function instances, and without a shared store each one keeps its own rate limit counters — so RATE_LIMIT_AI_GLOBAL=10 becomes "10 per instance per hour" rather than a real cap on AI provider spend. See Caching.

Migrations on Vercel

Migrations run as part of the build script. On Vercel they are applied for production deployments only, so a preview build cannot migrate a schema that production is still serving against — the common case when previews and production share one DATABASE_URL.

If your previews point at their own branch database and you want them migrated too, set RUN_MIGRATIONS=true for the Preview environment.

Monorepo build settings

Each app is its own Vercel project with Root Directory set to apps/web or apps/docs. Vercel reads vercel.json from that directory, so per-app settings belong in apps/web/vercel.json and apps/docs/vercel.json — a vercel.json at the repository root is ignored under this layout.

Both apps set installCommand: pnpm install --frozen-lockfile, so a stale lockfile fails the build instead of silently resolving different versions than CI did.

Neither sets an ignoreCommand. Skipping builds for commits that don't touch an app is handled by Vercel's built-in Settings → Git → Skip unaffected projects, which supersedes the deprecated turbo-ignore. Note that an ignoreCommand in vercel.json takes precedence over that setting, so don't reintroduce one without removing the toggle.

Function duration

AI quiz generation runs as a server action on the quiz create and edit routes, which set maxDuration = 300. A long generation with image inputs can otherwise be cut off by the platform default mid-request.

Health Checks

The app automatically checks database connectivity. If the database is unavailable, pages will display an error message rather than crashing.

Reverse Proxy Configuration

Nginx

server {
    listen 80;
    server_name quiz.example.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Caddy

quiz.example.com {
    reverse_proxy localhost:3000
}

Troubleshooting

Application won't start

  1. Check all required environment variables are set
  2. Verify database connectivity with pnpm db:studio
  3. Check logs for specific error messages

Authentication issues

  1. Verify OIDC issuer URL is accessible
  2. Check callback URL is configured in your identity provider
  3. Ensure BETTER_AUTH_URL matches your deployment URL

Database connection errors

  1. Verify DATABASE_URL format is correct
  2. Check database server is running and accessible
  3. Ensure database user has proper permissions

See database.md for detailed database troubleshooting.

On this page