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>
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:
- Backend API:
https://convex-backend.mentat.ovh:3210 - 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.
- Get your API key from resend.com
- Verify your domain in Resend and create a sender email (e.g.
noreply@yourdomain.com) - 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.tsat 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
callbackURLstarts with/ - Ensure
sign-inandsign-uppages are not protected byisAuthenticated() - Check
NEXT_PUBLIC_SITE_URLmatches your actual URL
Locale not switching
- Check
src/proxy.tsmatcher includes the route - Check browser has
NEXT_LOCALEcookie - Ensure both
messages/en.jsonandmessages/pl.jsonexist 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.jsonname - Update
README.mdtitle and description - Fill
.env.local - Deploy Convex backend on Coolify
- Set
BETTER_AUTH_SECRETandSITE_URLon Convex - Set
RESEND_API_KEYandRESEND_FROM_EMAILon Convex - Update
src/app/layout.tsxmetadata - Remove/replace placeholder content in
src/app/[locale]/page.tsx - Add project-specific routes to
src/lib/routes.ts - Run
pnpm lintandpnpm buildto verify