# Self-Hosting Guide Deploy Confabulous Code — self-hosted analytics for your Claude Code and Codex sessions — on your own infrastructure. This guide walks you through setup step by step. For the full environment variable reference, see [CONFIGURATION.md](CONFIGURATION.md). For security details, see [backend/SECURITY.md](backend/SECURITY.md). > **Worked example:** [`confab-demo-site`](https://github.com/ConfabulousDev/confab-demo-site) — live compose, Caddyfile, and OpenTofu behind [code-demo.confabulous.dev](https://code-demo.confabulous.dev) on a $7/mo Linode. Tracks this guide. --- ## Prerequisites - **Docker** and **Docker Compose** v2+ - A server with at least **1 GB RAM** and **1 CPU** (VPS, home lab, cloud VM) - A **domain name** (optional but recommended for HTTPS) --- ## Quickstart Get an instance running in under a minute. The repo ships a ready-to-run `docker-compose.yml` with safe localhost defaults — no configuration needed to kick the tires. **1. Clone the repo:** ```bash git clone https://github.com/ConfabulousDev/confab-web.git cd confab-web ``` **2. Start the stack:** ```bash docker compose up -d ``` This pulls the prebuilt image and starts the full stack — app, background worker, PostgreSQL, and MinIO — wiring up the database and storage bucket automatically. **3. Open the dashboard:** Visit [http://localhost:8080](http://localhost:8080) and log in with `admin@local.dev` / `localdevpassword`. **4. Connect the CLI:** ```bash curl -fsSL https://raw.githubusercontent.com/ConfabulousDev/confab/main/install.sh | bash confab setup --backend-url http://localhost:8080 ``` Start a Claude Code, Codex, or OpenCode session — it appears in the dashboard automatically. > The Quickstart defaults are for evaluation only: insecure-cookie mode is on and > the app is published on `127.0.0.1` (localhost) so it isn't exposed to the > network. Work through **Production Setup** before exposing it to the internet. --- ## Production Setup The root `docker-compose.yml` reads every operator-facing value from a `.env` file next to it. You configure a real deployment by editing `.env` — you don't edit the compose file. ```bash cp .env.example .env ``` `.env.example` is organized by section (secrets, URLs, auth, team, smart recaps, email, …) with every variable documented. Uncomment and set what you need, then restart with `docker compose up -d`. ### Generate secrets ```bash openssl rand -base64 32 # CSRF_SECRET_KEY (must be ≥ 32 chars) openssl rand -base64 24 # each of POSTGRES_PASSWORD / MINIO_ROOT_USER / MINIO_ROOT_PASSWORD ``` Set them in `.env`: ```bash CSRF_SECRET_KEY=<32+ char random> POSTGRES_PASSWORD= MINIO_ROOT_USER= MINIO_ROOT_PASSWORD= ``` These thread through every service automatically — the bundled Postgres and MinIO pick them up, and the app's `DATABASE_URL` and S3 credentials are derived from them. The Quickstart defaults (`confab` / `minioadmin`) are only reachable on the Docker network, but default credentials are bad hygiene — replace them. ### Set public URLs and turn off dev mode ```bash FRONTEND_URL=https://confab.example.com BACKEND_URL=https://confab.example.com ALLOWED_ORIGINS=https://confab.example.com INSECURE_DEV_MODE=false ``` All three URLs are typically the same value. They may differ if you run the frontend and backend on separate domains. ### Admin bootstrap ```bash ADMIN_BOOTSTRAP_EMAIL=admin@example.com ADMIN_BOOTSTRAP_PASSWORD=a-strong-password SUPER_ADMIN_EMAILS=admin@example.com ``` The bootstrap credentials create an admin user on first startup when no users exist. > **Important:** After logging in for the first time and confirming your account > works, remove `ADMIN_BOOTSTRAP_EMAIL` and `ADMIN_BOOTSTRAP_PASSWORD` from `.env` > and restart. These are only needed for initial setup. ### External PostgreSQL (optional) To use a managed database (AWS RDS, DigitalOcean, Supabase, etc.) instead of the bundled Postgres: 1. Set `DATABASE_URL` in `.env`: ```bash DATABASE_URL=postgres://user:password@db-host:5432/confab?sslmode=require ``` (For a separate migration user, also set `MIGRATE_DATABASE_URL`.) 2. Remove the `postgres` service and `postgres_data` volume from `docker-compose.yml`. ### External S3 storage (optional) To use AWS S3, DigitalOcean Spaces, Wasabi, or another S3-compatible provider instead of MinIO: 1. Set the storage variables in `.env`: ```bash S3_ENDPOINT=s3.amazonaws.com # or your provider's endpoint, no http(s):// prefix S3_USE_SSL=true AWS_ACCESS_KEY_ID=your-access-key AWS_SECRET_ACCESS_KEY=your-secret-key BUCKET_NAME=your-bucket-name ``` 2. Remove the `minio`, `minio-setup` services and `minio_data` volume from `docker-compose.yml`. --- ## HTTPS with Caddy The compose file includes a [Caddy](https://caddyserver.com/) reverse proxy behind a `caddy` profile. Caddy automatically provisions TLS certificates via Let's Encrypt — no extra files to add, no port mappings to remove. **1. Set your domain** in `.env`: ```bash CONFAB_DOMAIN=confab.example.com FRONTEND_URL=https://confab.example.com BACKEND_URL=https://confab.example.com ALLOWED_ORIGINS=https://confab.example.com INSECURE_DEV_MODE=false ``` **2. Point your DNS** A record at your server's IP. **3. Start with the Caddy profile:** ```bash docker compose --profile caddy up -d ``` Caddy obtains a certificate for `CONFAB_DOMAIN` and reverse-proxies it to the app. The bundled [`Caddyfile`](Caddyfile) handles TLS, gzip/zstd compression, and rotating access logs; edit it only if you need custom proxy behavior. --- ## Authentication At least one authentication method must be enabled. You can enable multiple methods simultaneously. All of these go in `.env`. ### Password Auth The simplest option — recommended for single-user or small team deployments. On by default: ```bash AUTH_PASSWORD_ENABLED=true ``` ### GitHub OAuth Create an OAuth app at [github.com/settings/developers](https://github.com/settings/developers): - **Homepage URL:** `https://confab.example.com` - **Authorization callback URL:** `https://confab.example.com/auth/github/callback` ```bash GITHUB_CLIENT_ID=your-client-id GITHUB_CLIENT_SECRET=your-client-secret GITHUB_REDIRECT_URL=https://confab.example.com/auth/github/callback ``` ### Google OAuth Create OAuth credentials at [console.cloud.google.com/apis/credentials](https://console.cloud.google.com/apis/credentials): - **Authorized redirect URI:** `https://confab.example.com/auth/google/callback` ```bash GOOGLE_CLIENT_ID=your-client-id GOOGLE_CLIENT_SECRET=your-client-secret GOOGLE_REDIRECT_URL=https://confab.example.com/auth/google/callback ``` ### Generic OIDC Works with Keycloak, Okta, Auth0, Azure AD, and any OpenID Connect provider. The provider must support OIDC Discovery (`/.well-known/openid-configuration`). All four variables must be set: ```bash OIDC_ISSUER_URL=https://your-idp.example.com OIDC_CLIENT_ID=your-client-id OIDC_CLIENT_SECRET=your-client-secret OIDC_REDIRECT_URL=https://confab.example.com/auth/oidc/callback OIDC_DISPLAY_NAME=SSO # Controls button text ("Continue with ...") ``` --- ## Single-Tenant / Single-Org Lockdown For an internal-only instance with no public signups, two variables lock the deployment down. Set both in `.env` for a fully closed instance. **Restrict who can log in** (applies to password, OAuth, and OIDC): ```bash ALLOWED_EMAIL_DOMAINS=company.com,partner.com ``` **Block new registrations** (existing users keep working; new sign-ups are rejected): ```bash MAX_USERS=0 ``` --- ## Team Settings Configure how your team interacts with sessions and sharing (all in `.env`). | Variable | What it does | |----------|-------------| | `SHARE_ALL_SESSIONS_TO_AUTHENTICATED` | Set to `true` to make every session visible to all authenticated users. Useful for small teams that want full transparency. | | `ENABLE_SHARE_CREATION` | Set to `true` to allow users to create external share links. | | `SHARE_DAILY_QUOTA` | Per-user cap on shares created in a rolling 24h window (default: `100`). The share-creation endpoint returns 429 once a user exceeds it. Set to `0` to disable the cap. | | `ENABLE_ORG_ANALYTICS` | Set to `true` to expose org-wide per-user analytics (`/admin/...`) to every authenticated user — same visibility model as `SHARE_ALL_SESSIONS_TO_AUTHENTICATED`. See [Organization Analytics in backend/API.md](backend/API.md#organization-analytics) for the privacy implications. | | `MAX_USERS` | Maximum registered users (default: `50`). Set to `0` to block new registrations. | | `SUPER_ADMIN_EMAILS` | Comma-separated emails with access to the admin panel at `/admin/users`. | --- ## Customization | Variable | What it does | |----------|-------------| | `SUPPORT_EMAIL` | Support email address shown in the UI. | ### Email (for Share Invitations) To enable email notifications for share invitations, sign up at [resend.com](https://resend.com) and add to `.env`: ```bash RESEND_API_KEY=re_xxxxxxxxxxxx EMAIL_FROM_ADDRESS=noreply@example.com ``` See [CONFIGURATION.md](CONFIGURATION.md) for additional email settings. --- ## Connecting the CLI Install the [Confab CLI](https://github.com/ConfabulousDev/confab): ```bash curl -fsSL https://raw.githubusercontent.com/ConfabulousDev/confab/main/install.sh | bash ``` Then configure it to point at your instance: ```bash confab setup --backend-url https://confab.example.com ``` The install script fetches pre-built binaries from [GitHub Releases](https://github.com/ConfabulousDev/confab/releases). Start a Claude Code or Codex session and it will appear in your dashboard automatically. --- ## Smart Recaps (Optional) AI-powered session summaries using the Anthropic or OpenAI API. Requires an [Anthropic API key](https://console.anthropic.com/) or an [OpenAI API key](https://platform.openai.com/api-keys). Add to `.env`: ```bash SMART_RECAP_ENABLED=true ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxx SMART_RECAP_MODEL=claude-haiku-4-5-20251001 SMART_RECAP_QUOTA_LIMIT=500 # Monthly per-user generation limit ``` Anthropic smart recaps support Claude Haiku 4.5, Sonnet 5, Opus 4.8, and Opus 5. Fable and Mythos models (thinking always on) are not supported. To use OpenAI instead: ```bash SMART_RECAP_ENABLED=true SMART_RECAP_LLM_PROVIDER=openai OPENAI_API_KEY=sk-xxxxxxxxxxxx SMART_RECAP_MODEL=gpt-6-luna ``` The bundled `worker` service precomputes recaps in the background. See [CONFIGURATION.md](CONFIGURATION.md) for advanced worker tuning options. --- ## Upgrading When a new version is released: ```bash # 1. Pull the latest images docker compose pull # 2. Run database migrations docker compose run --rm migrate # 3. Restart services with the new images docker compose up -d ``` Migrations are idempotent — safe to run multiple times. The `migrate` service exits after completion. (If you run with HTTPS, keep using `--profile caddy` on the `up` command.) --- ## Troubleshooting ### CORS errors in the browser console `ALLOWED_ORIGINS` must exactly match the URL in your browser's address bar, including the scheme (`https://`) and port (if non-standard). No trailing slash. ### OAuth callback fails with "redirect URI mismatch" The redirect URL in your OAuth provider's settings must exactly match the environment variable (`GITHUB_REDIRECT_URL`, `GOOGLE_REDIRECT_URL`, or `OIDC_REDIRECT_URL`), including the scheme and path. ### S3 / MinIO connection errors - `S3_ENDPOINT` should **not** include `http://` or `https://` — just the host and port (e.g., `minio:9000`). - Set `S3_USE_SSL` to `false` for local MinIO, `true` for external providers. - Ensure the bucket exists. The `minio-setup` service creates it automatically for local MinIO. ### "No authentication methods enabled" At least one auth method must be configured. Set `AUTH_PASSWORD_ENABLED=true` or configure an OAuth/OIDC provider. ### Cookies not persisting / login loop Without HTTPS, you must set `INSECURE_DEV_MODE=true`. In production, use HTTPS (see [HTTPS with Caddy](#https-with-caddy)) and ensure `INSECURE_DEV_MODE` is `false`. ### Database connection refused - Verify the `DATABASE_URL` is correct and the Postgres server is reachable from the Docker network. - If using the bundled Postgres, ensure the `postgres` service is healthy: `docker compose ps`. ### Port 8080 already in use Set `PORT` in `.env` to a free port — the published localhost port follows it automatically. --- ## Security Checklist Before exposing your instance to the internet: - [ ] `INSECURE_DEV_MODE` is `false` - [ ] `CSRF_SECRET_KEY` is a unique random string (32+ characters) - [ ] `POSTGRES_PASSWORD` and `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` are random values, not the Quickstart defaults - [ ] `ALLOWED_ORIGINS` contains only your domain - [ ] HTTPS is enforced (via the Caddy profile or another reverse proxy) - [ ] Bootstrap credentials (`ADMIN_BOOTSTRAP_*`) are removed after setup - [ ] Database uses SSL (`sslmode=require` in `DATABASE_URL`) if external - [ ] OAuth secrets are production values (not development/test credentials) For a comprehensive security review, see [backend/SECURITY.md](backend/SECURITY.md).