# Convex Turbo Monorepo A production-ready Turborepo starter with Next.js, Expo, and self-hosted Convex backend. Built with TypeScript, Tailwind CSS, and modern tooling. --- ## What's Inside? ### Apps & Packages - **`apps/next`** - Next.js 16 web application with App Router - **`apps/expo`** - Expo 54 mobile application _(in progress)_ - **`@gib/backend`** - Self-hosted Convex backend with authentication - **`@gib/ui`** - Shared shadcn/ui component library - **`@gib/eslint-config`** - ESLint configuration - **`@gib/prettier-config`** - Prettier configuration with import sorting - **`@gib/tailwind-config`** - Tailwind CSS v4 configuration - **`@gib/tsconfig`** - Shared TypeScript configurations ### Tech Stack - **Framework:** Next.js 16 (App Router) + Expo 54 - **Backend:** Convex (self-hosted) - **Auth:** @convex-dev/auth with Authentik OAuth & Password providers - **Styling:** Tailwind CSS v4 + shadcn/ui - **Language:** TypeScript (strict mode) - **Package Manager:** Bun - **Monorepo:** Turborepo - **Deployment:** Docker (standalone) --- ## Getting Started This is a self-hosted template. The full setup requires a server (home server or VPS) to host the Convex backend and dashboard, and a reverse proxy (nginx-proxy-manager is recommended) to expose them over HTTPS. The Next.js app can run locally in dev mode once the Convex containers are reachable. ### Prerequisites - [Bun](https://bun.sh) (v1.2+) - [Docker](https://www.docker.com/) & Docker Compose (for self-hosted Convex) - Node.js 22+ (for compatibility) - A running nginx-proxy-manager instance (or similar reverse proxy) to expose Convex over HTTPS --- ### Step 1 — Clone & Install ```bash git clone https://git.gbrown.org/gib/convex-monorepo cd convex-monorepo bun install ``` If you're using this as a template for a new project, remove the existing remote and add your own: ```bash git remote remove origin git remote add origin https://your-git-host.com/your/new-repo.git ``` --- ### Step 2 — Configure the Docker Environment The `docker/` directory contains everything needed to run the Convex backend and the Next.js app in production. ```bash cd docker/ cp .env.example .env ``` Edit `docker/.env` and fill in your values. The variables below the Next.js app section control the Convex containers — you'll need to choose: - `INSTANCE_NAME` — a unique name for your Convex instance - `INSTANCE_SECRET` — a secret string (generate something random) - `CONVEX_CLOUD_ORIGIN` — the public HTTPS URL for your Convex backend API (e.g. `https://api.convex.example.com`) - `CONVEX_SITE_ORIGIN` — the public HTTPS URL for Convex Auth HTTP routes (e.g. `https://api.convex.example.com`) - `NEXT_PUBLIC_DEPLOYMENT_URL` — the URL for the Convex dashboard (e.g. `https://dashboard.convex.example.com`) --- ### Step 3 — Start the Convex Containers ```bash cd docker/ sudo docker compose up -d convex-backend convex-dashboard ``` Wait a moment for `convex-backend` to pass its health check, then verify both containers are running: ```bash sudo docker compose ps ``` Reverse-proxy the two Convex services through nginx-proxy-manager (or your preferred proxy) to the URLs you chose in Step 2. Both must be reachable over HTTPS before you can proceed. --- ### Step 4 — Generate the Convex Admin Key With the backend container running, generate the admin key: ```bash cd docker/ ./generate_convex_admin_key ``` Copy the printed key — you'll need it as `CONVEX_SELF_HOSTED_ADMIN_KEY` in the root `.env` file. --- ### Step 5 — Configure Root Environment Variables Create the root `.env` file: ```bash # From the repo root cp .env.example .env ``` Fill out all values in `/.env`: ```bash # Next.js NODE_ENV=development SENTRY_AUTH_TOKEN= # From your self-hosted Sentry NEXT_PUBLIC_SITE_URL=https://example.com NEXT_PUBLIC_CONVEX_URL=https://api.convex.example.com NEXT_PUBLIC_PLAUSIBLE_URL=https://plausible.example.com NEXT_PUBLIC_SENTRY_DSN= NEXT_PUBLIC_SENTRY_URL=https://sentry.example.com NEXT_PUBLIC_SENTRY_ORG=sentry NEXT_PUBLIC_SENTRY_PROJECT_NAME=my-project # Convex CONVEX_SELF_HOSTED_URL=https://api.convex.example.com CONVEX_SELF_HOSTED_ADMIN_KEY= # From Step 4 CONVEX_SITE_URL=http://localhost:3000 # Always localhost:3000 for local dev # Auth (synced to Convex in Step 6) USESEND_API_KEY= USESEND_URL=https://usesend.example.com USESEND_FROM_EMAIL=My App AUTH_AUTHENTIK_ID= AUTH_AUTHENTIK_SECRET= AUTH_AUTHENTIK_ISSUER=https://auth.example.com/application/o/my-app/ ``` --- ### Step 6 — Generate JWT Keys & Sync Environment Variables to Convex Generate the RS256 JWT keypair needed for Convex Auth: ```bash cd packages/backend bun run scripts/generateKeys.mjs ``` This prints `JWT_PRIVATE_KEY` and `JWKS` values. Sync them to your Convex deployment along with all other backend environment variables: ```bash # From packages/backend/ bun with-env npx convex env set JWT_PRIVATE_KEY "your-private-key" bun with-env npx convex env set JWKS "your-jwks" bun with-env npx convex env set AUTH_AUTHENTIK_ID "your-client-id" bun with-env npx convex env set AUTH_AUTHENTIK_SECRET "your-client-secret" bun with-env npx convex env set AUTH_AUTHENTIK_ISSUER "your-issuer-url" bun with-env npx convex env set USESEND_API_KEY "your-api-key" bun with-env npx convex env set USESEND_URL "https://usesend.example.com" bun with-env npx convex env set USESEND_FROM_EMAIL "My App " ``` **For production auth to work**, you must also update `CONVEX_SITE_URL` in the Convex Dashboard to your production Next.js URL. Go to `https://dashboard.convex.example.com` → Settings → Environment Variables and set: ``` CONVEX_SITE_URL = https://example.com ``` The root `.env` value of `http://localhost:3000` is correct for local dev and should not be changed — only update it in the Dashboard for production. --- ### Step 7 — Start the Development Server ```bash # From project root bun dev:next # Next.js app + Convex backend (most common) # or bun dev # All apps (Next.js + Expo + Backend) ``` **App URLs:** - **Next.js:** http://localhost:3000 - **Convex Dashboard:** https://dashboard.convex.example.com --- ## Development Commands ```bash # Development bun dev # Run all apps bun dev:next # Next.js + Convex backend bun dev:expo # Expo + Convex backend bun dev:backend # Convex backend only # Quality Checks bun typecheck # Type checking (recommended for testing) bun lint # Lint all packages bun lint:fix # Lint and auto-fix bun format # Check formatting bun format:fix # Fix formatting # Build bun build # Build all packages (production) # Utilities bun ui-add # Add shadcn/ui components bun clean # Clean node_modules bun clean:ws # Clean workspace caches ``` ### Single Package Commands Use Turborepo filters to target specific packages: ```bash bun turbo run dev -F @gib/next # Run Next.js only bun turbo run typecheck -F @gib/backend # Typecheck backend bun turbo run lint -F @gib/ui # Lint UI package ``` --- ## Project Structure ``` convex-monorepo/ ├── apps/ │ ├── next/ # Next.js web app │ │ ├── src/ │ │ │ ├── app/ # App Router pages │ │ │ ├── components/ # React components │ │ │ └── lib/ # Utilities │ │ └── package.json │ └── expo/ # Expo mobile app (WIP) │ ├── packages/ │ ├── backend/ # Convex backend │ │ ├── convex/ # Convex functions (synced to deployment) │ │ ├── scripts/ # Utilities (generateKeys.mjs) │ │ └── types/ # Shared types │ └── ui/ # shadcn/ui components │ └── src/ # Component source files │ ├── tools/ # Shared configurations │ ├── eslint/ │ ├── prettier/ │ ├── tailwind/ │ └── typescript/ │ ├── docker/ # Self-hosted deployment │ ├── compose.yml │ ├── Dockerfile │ └── .env.example │ ├── turbo.json # Turborepo configuration └── package.json # Root workspace & catalogs ``` --- ## Features ### Authentication - **OAuth:** Authentik SSO integration - **Password:** Custom password auth with email verification - **OTP:** Email verification via self-hosted UseSend - **Session Management:** Secure cookie-based sessions (30-day max age) ### Next.js App - **App Router:** Next.js 16 with React Server Components - **Data Preloading:** SSR data fetching with `preloadQuery` + `usePreloadedQuery` - **Middleware:** Route protection & IP-based security (`src/proxy.ts`) - **Styling:** Tailwind CSS v4 with dark mode (OKLCH-based theme) - **Analytics:** Plausible (privacy-focused, proxied through Next.js) - **Monitoring:** Sentry error tracking & performance ### Backend - **Real-time:** Convex reactive queries - **File Storage:** Built-in file upload/storage - **Auth:** Multi-provider authentication - **Type-safe:** Full TypeScript with generated types - **Self-hosted:** Complete control over your data ### Developer Experience - **Monorepo:** Turborepo for efficient builds and caching - **Type Safety:** Strict TypeScript throughout - **Code Quality:** ESLint + Prettier with auto-fix - **Hot Reload:** Fast refresh for all packages - **Catalog Deps:** Centralized dependency version management --- ## Deployment ### Production Deployment (Docker) Once the Convex containers are running (they only need to be started once), deploying a new version of the Next.js app is a two-command workflow: ```bash # SSH onto your server, then: cd docker/ # Build the new image sudo docker compose build next-app # Deploy it sudo docker compose up -d next-app ``` To start all services from scratch: ```bash cd docker/ sudo docker compose up -d convex-backend convex-dashboard # Wait for backend health check to pass, then: sudo docker compose up -d next-app ``` **Services:** - `next-app` — Next.js standalone build - `convex-backend` — Convex backend (port 3210) - `convex-dashboard` — Admin dashboard (port 6791) **Network:** Uses `nginx-bridge` Docker network (reverse proxy via nginx-proxy-manager). ### Production Checklist - [ ] Fill out `docker/.env` with your domain names and secrets - [ ] Start `convex-backend` and `convex-dashboard` containers - [ ] Generate and set `CONVEX_SELF_HOSTED_ADMIN_KEY` via `./generate_convex_admin_key` - [ ] Reverse-proxy both Convex services via nginx-proxy-manager with SSL - [ ] Fill out root `/.env` with all environment variables - [ ] Generate JWT keys and sync all env vars to Convex (`bun with-env npx convex env set ...`) - [ ] Update `CONVEX_SITE_URL` in the Convex Dashboard to your production Next.js URL - [ ] Build and start the `next-app` container - [ ] Back up `docker/data/` regularly (contains all Convex database data) --- ## Documentation - **[AGENTS.md](./AGENTS.md)** — Comprehensive guide for AI agents & developers - **[Convex Docs](https://docs.convex.dev)** — Official Convex documentation - **[Turborepo Docs](https://turbo.build/repo/docs)** — Turborepo documentation - **[Next.js Docs](https://nextjs.org/docs)** — Next.js documentation --- ## Troubleshooting ### Backend typecheck shows TypeScript help message This is expected behavior. The backend package follows Convex's structure with only `convex/tsconfig.json` (no root tsconfig). Running `bun typecheck` from the repo root will show TypeScript's help text for `@gib/backend` — this is not an error. ### Imports from Convex require `.js` extension The project uses ESM (`"type": "module"`), which requires explicit file extensions: ```typescript // ✅ Correct import type { Id } from '@gib/backend/convex/_generated/dataModel.js'; // ❌ Wrong — will fail at runtime import { api } from '@gib/backend/convex/_generated/api'; import { api } from '@gib/backend/convex/_generated/api.js'; ``` ### Docker containers won't start 1. Check Docker logs: `sudo docker compose logs` 2. Verify environment variables in `docker/.env` 3. Ensure the `nginx-bridge` network exists: `sudo docker network create nginx-bridge` 4. Check that the required ports (3210, 6791) are not already in use ### Auth doesn't work in production Make sure `CONVEX_SITE_URL` is set to your production Next.js URL in the **Convex Dashboard** (not just in the root `.env` file). The root `.env` should always contain `http://localhost:3000`; the Dashboard must have your production URL. ### Catalog updates break workspace After updating dependencies, if you see `sherif` errors on `bun install`: 1. Never use `bun update` inside individual package directories 2. Edit the version in root `package.json` catalog section instead 3. Run `bun install` from the root 4. Verify with `bun lint:ws` --- ## Contributing This is a personal monorepo template. Feel free to fork and adapt for your needs. ### Code Style - Single quotes, trailing commas - 80 character line width - ESLint + Prettier enforced - `const fn = () => {}` over `function fn()` (strong preference) - Import order: Types → React → Next → Third-party → @gib → Local Run `bun lint:fix` and `bun format:fix` before committing. --- ## License MIT --- ## Acknowledgments Built with inspiration from: - [T3 Turbo](https://github.com/t3-oss/create-t3-turbo) - [Convex](https://convex.dev) - [shadcn/ui](https://ui.shadcn.com) - [Turborepo](https://turbo.build)