Skip to content
On this page

Self-Hosting

Self-hosting gives you full control over your data and billing while matching the hosted experience.

What you’ll need

  • Convex CLI (npm i -g convex)
  • Access to secrets for the features you want: Cloudflare Workers AI (AI), Kernel (screenshots), Resend (email), Polar (billing)

Quickstart (local)

Clone and run setup

Clone the repo, then run the idempotent bootstrap:

bun run setup

Setup installs dependencies, provisions an isolated Convex development deployment, sets the local SITE_URL and JWKS defaults, pushes backend code, and derives apps/web/.env.local — no OAuth or billing credentials required. Verify with bun run doctor.

Start a Convex dev deployment

bunx convex dev

This keeps the dev deployment in sync and prints the CONVEX_SITE_URL and CONVEX_URL. It does not configure Better Auth — that’s the next step.

Configure essential secrets

Run these commands to set the minimum required secrets in your Convex dashboard (SITE_URL is already set by setup):

# Security & Auth
npx convex env set BETTER_AUTH_SECRET $(openssl rand -base64 32)
npx convex env set TEAK_ADMIN_EMAIL you@example.com

# Optional: AI (if you want card processing)
npx convex env set CLOUDFLARE_ACCOUNT_ID your-account-id
npx convex env set CLOUDFLARE_API_TOKEN your-api-token

# Optional: Google sign-in (both or neither; email works without them)
npx convex env set GOOGLE_CLIENT_ID your-client-id
npx convex env set GOOGLE_CLIENT_SECRET your-client-secret

Create local env files

Copy the Convex URLs from your dashboard into each app’s .env.local. Each app uses a framework-specific prefix for the same two vars:

App Prefix
apps/web NEXT_PUBLIC_
apps/mobile EXPO_PUBLIC_
apps/extension, apps/desktop VITE_PUBLIC_

See Detailed Environment Reference for the full variable list.

Start the stack

bun run dev

Production basics

Designate the administrator

Set TEAK_ADMIN_EMAIL on the production Convex deployment to the normalized email address of the account that should receive administrative access. Teak fails closed when this value is absent or does not match a registered user; account creation order never grants administrative access.

Update SITE_URL

Update SITE_URL in Convex to your public domain (e.g., https://app.yourdomain.com).

Point clients at production

Update CONVEX_SITE_URL and CONVEX_URL in your client env files to point to your production deployment.

Host the web app

Point your domain’s SITE_URL to wherever you are hosting the Next.js app.

Add optional services

Add email (RESEND_API_KEY) and billing (Polar) keys to Convex env if needed.

Build and start

bun run build
bun run start

Detailed Environment Reference

Add these to your env files before running locally or deploying.

Backend (packages/convex/.env.local)

SITE_URL=http://localhost:3000
JWKS=null
PUBLIC_ORIGIN=https://yourdomain.com
BETTER_AUTH_SECRET=your-secret
TEAK_ADMIN_EMAIL=you@example.com
FILES_BASE=https://files.yourdomain.com
FILES_SIGNING_SECRET=shared-secret
R2_ACCESS_KEY_ID=access-key
R2_SECRET_ACCESS_KEY=secret-key
R2_ENDPOINT=https://account.r2.cloudflarestorage.com
R2_BUCKET=teak-files
KERNEL_API_KEY=token
CLOUDFLARE_ACCOUNT_ID=your-account-id
CLOUDFLARE_API_TOKEN=your-api-token
POLAR_ACCESS_TOKEN=token
POLAR_ORGANIZATION_TOKEN=token
POLAR_SERVER=sandbox
POLAR_WEBHOOK_SECRET=secret
RESEND_API_KEY=token

SITE_URL is required to boot; BETTER_AUTH_SECRET is required for authentication, while TEAK_ADMIN_EMAIL is only needed for admin access. Add the rest for the features you want. See Where to get the values for sources.

Files Worker (apps/files-worker/.dev.vars, wrangler secrets)

The Files Worker owns all file-byte operations and binds to your R2 bucket plus Cloudflare Images and Workers AI (all declared in wrangler.jsonc — set bucket_name to your own bucket):

FILES_SIGNING_SECRET=shared-secret           # Must match the Convex value

Deploy it with bunx wrangler deploy from apps/files-worker, then point FILES_BASE in the backend env at the worker’s public origin.

Web (apps/web/.env.local)

CONVEX_DEPLOY_KEY=
NEXT_PUBLIC_CONVEX_URL=https://deployment.convex.cloud
NEXT_PUBLIC_CONVEX_SITE_URL=https://deployment.convex.site
NEXT_PUBLIC_FILES_BASE=https://files.yourdomain.com            # Mirror of FILES_BASE for the CSP
TEAK_DEV_APP_URL=http://localhost:3000   # Optional local override

The web Content-Security-Policy always allows the canonical file origins (https://files.teakvault.com and the R2 storage origin). If you serve files from custom origins, mirror the backend FILES_BASE into NEXT_PUBLIC_FILES_BASE at web build time.

Client Apps (apps/mobile, apps/extension, apps/desktop)

Mobile, Extension, and Desktop share the same base variables (use EXPO_PUBLIC_ prefix for mobile, VITE_PUBLIC_ for extension and desktop):

VITE_PUBLIC_CONVEX_URL=https://deployment.convex.cloud
VITE_PUBLIC_CONVEX_SITE_URL=https://deployment.convex.site
TEAK_DEV_APP_URL=http://localhost:3000   # Optional local override

Desktop-only

The desktop app additionally reads VITE_WEB_URL to know where to send sign-in and account links. Mobile and the extension do not use this variable.

VITE_WEB_URL=https://app.yourdomain.com   # Defaults to https://app.teakvault.com

Public API and MCP

PUBLIC_ORIGIN=https://yourdomain.com            # Required to expose self-hosted /api and /mcp (else production URLs are advertised)
TEAK_DEV_API_URL=https://deployment.convex.site # Optional local override

Convex serves the REST API at /api/v1, the MCP endpoint at /mcp, plus /healthz, /openapi.json, and OAuth protected-resource metadata. In production, Teak exposes these through apex path rewrites (e.g. https://teakvault.com/api and https://teakvault.com/mcp). For self-hosted setups, configure your reverse proxy or DNS to route /api and /mcp to the Convex deployment’s .convex.site domain. SITE_URL is the Better Auth application origin and the OAuth issuer; there is no separate issuer override.

Where to get the values

  • Convex URLs: Run bunx convex dev. It prints both CONVEX_URL (for the client) and CONVEX_SITE_URL (for the site/auth endpoint). You can also find these in the Convex Dashboard under “Settings” -> “Deployment”.
  • Better Auth:
    • BETTER_AUTH_SECRET: Generate a random string (e.g., via openssl rand -base64 32).
    • SITE_URL: This is the URL where your Next.js app is running. Locally, it’s http://localhost:3000.
    • TEAK_ADMIN_EMAIL: The normalized email address of the one account allowed to use administrative functions. Configure this independently for development and production.
  • Optional Features:
    • R2_*: Create a private Cloudflare R2 bucket and an API token/S3 credentials with object read, write, and delete access.
    • FILES_BASE + FILES_SIGNING_SECRET: Deploy the Files Worker (see its section above); card uploads, downloads, thumbnails, and AI analysis all flow through it.
    • CLOUDFLARE_ACCOUNT_ID + CLOUDFLARE_API_TOKEN: Get from the Cloudflare dashboard — create an API token with Workers AI run permission for AI-powered card processing.
    • KERNEL_API_KEY: Get from Kernel for automated link screenshots.
    • RESEND_API_KEY: Get from Resend for email verification and password resets.
    • POLAR_*: Get from Polar if you want to use the built-in billing system.

Last updated on September 18, 2026

Was this page helpful?