t-convex-nextjs-saas/DEVELOPMENT.md
nxtkofi 4ae60473b6 docs: rewrite README and update DEVELOPMENT guide
Rewrite README.md as a clean template landing page. Update DEVELOPMENT.md with Resend setup instructions and expanded checklist.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-21 21:37:24 +02:00

5.4 KiB

Development Guide

Personal cheat sheet for bootstrapping and deploying projects from this template.

Local Development Setup

1. Clone & Install

git clone <your-forgejo-repo> my-project
cd my-project
pnpm install

2. Environment Variables

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

CONVEX_SELF_HOSTED_URL='https://convex-backend.mentat.ovh'
CONVEX_SELF_HOSTED_ADMIN_KEY='self-hosted-convex|...'
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_CONVEX_URL=https://convex-backend.mentat.ovh
NEXT_PUBLIC_CONVEX_SITE_URL=https://backend-site-xxx.mentat.ovh

Never commit .env.local — it's in .gitignore.

3. Run Dev Server

pnpm dev --webpack

⚠️ Never use Turbopack — Next.js 16.2.1 has a CPU spike bug. Always --webpack.

4. Convex Setup (Local)

npx convex dev

This connects to your self-hosted Convex instance. Ensure CLI version matches your backend image version.

Coolify Deployment (Convex Self-Hosted)

1. Deploy Convex Backend

On Coolify, create a new service using this docker-compose:

services:
  backend:
    image: 'ghcr.io/get-convex/convex-backend:latest'
    stop_grace_period: 10s
    stop_signal: SIGINT
    ports:
      - '${PORT:-3210}:3210'
      - '${SITE_PROXY_PORT:-3211}:3211'
    volumes:
      - 'data:/convex/data'
    environment:
      INSTANCE_NAME: '${INSTANCE_NAME}'
      INSTANCE_SECRET: '${INSTANCE_SECRET}'
      CONVEX_CLOUD_ORIGIN: '${SERVICE_URL_BACKEND}'
      CONVEX_SITE_ORIGIN: '${SERVICE_URL_BACKEND_SITE}'
      DO_NOT_REQUIRE_SSL: '${DO_NOT_REQUIRE_SSL:-true}'
      DATABASE_URL: '${DATABASE_URL:-}'
      # ... (see README.md for full config)
    healthcheck:
      test: ['CMD-SHELL', 'curl -f http://localhost:3210/version']
      interval: 5s
      start_period: 10s
      timeout: 5s
      retries: 10
  dashboard:
    image: 'ghcr.io/get-convex/convex-dashboard:latest'
    stop_grace_period: 10s
    stop_signal: SIGINT
    ports:
      - '${DASHBOARD_PORT:-6791}:6791'
    environment:
      PORT: '6791'
      NEXT_PUBLIC_DEPLOYMENT_URL: '${SERVICE_URL_BACKEND}'
    depends_on:
      backend:
        condition: service_healthy
volumes:
  data: null

Best practice: Copy the official self-hosted config and adapt env vars for Coolify.

2. Generate Admin Key

SSH into the server and run:

docker exec -it <backend-container-name> ./generate_admin_key.sh

Save the key. You'll use it to log into the Convex dashboard.

3. Configure Domains

Add 2 domains in Coolify for the backend:

  1. Backend API: https://convex-backend.mentat.ovh:3210
  2. Actions proxy: https://backend-site-xxx.mentat.ovh:3211

4. Better Auth Env Vars

Set these on your Convex backend via CLI:

pnpm dlx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
pnpm dlx convex env set SITE_URL=http://localhost:3000

For production, SITE_URL should be your deployed frontend URL.

5. Resend Email Setup

Email is handled by Resend. The API key is server-side only and lives on Convex, not in .env.local.

  1. Get your API key from resend.com
  2. Verify your domain in Resend and create a sender email (e.g. noreply@yourdomain.com)
  3. Set Convex environment variables:
pnpm dlx convex env set RESEND_API_KEY=re_xxxxxxxxxxxxx
pnpm dlx convex env set RESEND_FROM_EMAIL=noreply@yourdomain.com

These are read by convex/lib/resend.ts at runtime. Never commit them.

Adding a New Project from Template

Option A: Manual Clone (Private Forgejo)

git clone <forgejo-url>/convex-next-saas.git new-project
cd new-project
# Remove .git and re-init
git remote remove origin
git init
git remote add origin <new-project-url>

Option B: Using the Init Script

node bin/init-template.mjs new-project

This will:

  • Copy the template to new-project/
  • Replace placeholders ({{project-name}}, {{site-url}})
  • Initialize a fresh git repo
  • Run pnpm install

Common Issues

missing field functions on npx convex dev

Your CLI and backend image versions differ. Check:

npx convex --version
# Compare with backend image tag in Coolify

Upgrade whichever is lower.

Auth redirect loop

  • Ensure callbackURL starts with /
  • Ensure sign-in and sign-up pages are not protected by isAuthenticated()
  • Check NEXT_PUBLIC_SITE_URL matches your actual URL

Locale not switching

  • Check src/proxy.ts matcher includes the route
  • Check browser has NEXT_LOCALE cookie
  • Ensure both messages/en.json and messages/pl.json exist and are valid JSON

Build fails with Turbopack

You forgot --webpack:

# Wrong
pnpm build

# Right
# build script in package.json already does next build
# dev only:
pnpm dev --webpack

Project Checklist

When starting a new project:

  • Clone template / run init script
  • Update package.json name
  • Update README.md title and description
  • Fill .env.local
  • Deploy Convex backend on Coolify
  • Set BETTER_AUTH_SECRET and SITE_URL on Convex
  • Set RESEND_API_KEY and RESEND_FROM_EMAIL on Convex
  • Update src/app/layout.tsx metadata
  • Remove/replace placeholder content in src/app/[locale]/page.tsx
  • Add project-specific routes to src/lib/routes.ts
  • Run pnpm lint and pnpm build to verify