Deployment walkthrough
This guide walks through deploying Confabulous step by step. For the full environment-variable reference, see Configuration. For real-world annotated configs, see Sample deployments.
Prerequisites
Section titled “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 strongly recommended for HTTPS).
1. Quickstart
Section titled “1. Quickstart”The repo ships a ready-to-run docker-compose.yml with safe localhost defaults, so the fastest path to a working instance is clone-and-run — handy for kicking the tires before customizing.
Clone the repo:
git clone https://github.com/ConfabulousDev/confab-web.gitcd confab-webStart the stack:
docker compose up -dThis pulls the prebuilt image and starts the full stack — app, background worker, PostgreSQL, and MinIO — wiring up the database and storage bucket automatically.
Open the dashboard:
Visit http://localhost:8080 and log in with admin@local.dev / localdevpassword.
Connect the CLI:
curl -fsSL https://raw.githubusercontent.com/ConfabulousDev/confab/main/install.sh | bashconfab setup --backend-url http://localhost:8080Start a Claude Code, Codex, or OpenCode session — it appears in the dashboard automatically.
2. Production setup
Section titled “2. 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.
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
Section titled “Generate secrets”openssl rand -base64 32 # CSRF_SECRET_KEY (must be ≥ 32 chars)openssl rand -base64 24 # each of POSTGRES_PASSWORD / MINIO_ROOT_USER / MINIO_ROOT_PASSWORDSet them in .env:
CSRF_SECRET_KEY=<32+ char random>POSTGRES_PASSWORD=<random>MINIO_ROOT_USER=<random>MINIO_ROOT_PASSWORD=<random>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
Section titled “Set public URLs and turn off dev mode”FRONTEND_URL=https://confab.example.comBACKEND_URL=https://confab.example.comALLOWED_ORIGINS=https://confab.example.comINSECURE_DEV_MODE=falseAll three URLs are typically the same value. They may differ if you run the frontend and backend on separate domains.
Admin bootstrap
Section titled “Admin bootstrap”ADMIN_BOOTSTRAP_EMAIL=admin@example.comADMIN_BOOTSTRAP_PASSWORD=a-strong-passwordSUPER_ADMIN_EMAILS=admin@example.comThe bootstrap credentials create an admin user on first startup when no users exist.
External PostgreSQL (optional)
Section titled “External PostgreSQL (optional)”To use a managed database (AWS RDS, DigitalOcean, Supabase, etc.) instead of the bundled Postgres:
-
Set
DATABASE_URLin.env(andMIGRATE_DATABASE_URLfor a separate migration user):Terminal window DATABASE_URL=postgres://user:password@db-host:5432/confab?sslmode=require -
Remove the
postgresservice andpostgres_datavolume fromdocker-compose.yml.
External S3 storage (optional)
Section titled “External S3 storage (optional)”To use AWS S3, DigitalOcean Spaces, Wasabi, or another S3-compatible provider instead of MinIO:
-
Set the storage variables in
.env:Terminal window S3_ENDPOINT=s3.amazonaws.com # or your provider's endpoint, no http(s):// prefixS3_USE_SSL=trueAWS_ACCESS_KEY_ID=your-access-keyAWS_SECRET_ACCESS_KEY=your-secret-keyBUCKET_NAME=your-bucket-name -
Remove the
minio,minio-setupservices andminio_datavolume fromdocker-compose.yml.
3. HTTPS with Caddy
Section titled “3. HTTPS with Caddy”The compose file includes a Caddy 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.
Set your domain in .env:
CONFAB_DOMAIN=confab.example.comFRONTEND_URL=https://confab.example.comBACKEND_URL=https://confab.example.comALLOWED_ORIGINS=https://confab.example.comINSECURE_DEV_MODE=falsePoint your DNS A record at your server’s IP, then start with the Caddy profile:
docker compose --profile caddy up -dCaddy obtains a certificate for CONFAB_DOMAIN and reverse-proxies it to the app. The bundled Caddyfile handles TLS, gzip/zstd compression, and rotating access logs; edit it only if you need custom proxy behavior.
4. Authentication
Section titled “4. Authentication”At least one authentication method must be enabled. You can enable multiple methods simultaneously. All of these go in .env.
Password auth
Section titled “Password auth”The simplest option — recommended for single-user or small-team deployments. On by default (AUTH_PASSWORD_ENABLED=true).
GitHub OAuth
Section titled “GitHub OAuth”Create an OAuth app at github.com/settings/developers:
- Homepage URL:
https://confab.example.com - Authorization callback URL:
https://confab.example.com/auth/github/callback
GITHUB_CLIENT_ID=your-client-idGITHUB_CLIENT_SECRET=your-client-secretGITHUB_REDIRECT_URL=https://confab.example.com/auth/github/callbackGoogle OAuth
Section titled “Google OAuth”Create OAuth credentials at console.cloud.google.com/apis/credentials:
- Authorized redirect URI:
https://confab.example.com/auth/google/callback
GOOGLE_CLIENT_ID=your-client-idGOOGLE_CLIENT_SECRET=your-client-secretGOOGLE_REDIRECT_URL=https://confab.example.com/auth/google/callbackGeneric OIDC
Section titled “Generic OIDC”Works with Keycloak, Okta, Auth0, Azure AD, and any OpenID Connect provider that supports OIDC Discovery (/.well-known/openid-configuration). All four variables must be set:
OIDC_ISSUER_URL=https://your-idp.example.comOIDC_CLIENT_ID=your-client-idOIDC_CLIENT_SECRET=your-client-secretOIDC_REDIRECT_URL=https://confab.example.com/auth/oidc/callbackOIDC_DISPLAY_NAME=SSO # Controls button text ("Continue with ...")5. Single-tenant / single-org lockdown
Section titled “5. 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):
ALLOWED_EMAIL_DOMAINS=company.com,partner.comBlock new registrations (existing users keep working; new sign-ups are rejected):
MAX_USERS=06. Team settings
Section titled “6. Team settings”| 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. See Sharing. |
ENABLE_SHARE_CREATION | Set to true to allow users to create external share links. |
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. |
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 for the privacy implications. |
7. Smart recaps (optional)
Section titled “7. Smart recaps (optional)”AI-powered session summaries using the Anthropic API. Requires an Anthropic API key. Add to .env:
SMART_RECAP_ENABLED=trueANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxSMART_RECAP_MODEL=claude-haiku-4-5-20251001SMART_RECAP_QUOTA_LIMIT=500 # Monthly per-user generation limitThe bundled worker service precomputes recaps in the background. See Configuration for advanced worker tuning options.
8. Email (optional, for share invitations)
Section titled “8. Email (optional, for share invitations)”Sign up at resend.com and add to .env:
RESEND_API_KEY=re_xxxxxxxxxxxxEMAIL_FROM_ADDRESS=noreply@example.comSee Configuration for additional email settings (rate limits, display name, support email).
9. Upgrading
Section titled “9. Upgrading”When a new version is released:
# 1. Pull the latest imagesdocker compose pull
# 2. Run database migrationsdocker compose run --rm migrate
# 3. Restart services with the new imagesdocker compose up -dMigrations are idempotent — safe to run multiple times. The migrate service exits after completion. If you run with HTTPS, keep --profile caddy on the up command.
10. Security checklist
Section titled “10. Security checklist”Before exposing your instance to the internet:
-
INSECURE_DEV_MODEisfalse. -
CSRF_SECRET_KEYis a unique random string of 32+ characters. -
POSTGRES_PASSWORDandMINIO_ROOT_USER/MINIO_ROOT_PASSWORDare random values, not the Quickstart defaults. -
ALLOWED_ORIGINScontains 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=requireinDATABASE_URL) if external. - OAuth secrets are production values, not development/test credentials.
For a comprehensive security review, see backend/SECURITY.md in the repo.
Troubleshooting
Section titled “Troubleshooting”CORS errors in the browser console
Section titled “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”
Section titled “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
Section titled “S3 / MinIO connection errors”S3_ENDPOINTmust not includehttp://orhttps://— just the host and port (e.g.minio:9000).- Set
S3_USE_SSLtofalsefor local MinIO,truefor external providers. - Ensure the bucket exists. The
minio-setupservice creates it automatically for local MinIO.
”No authentication methods enabled”
Section titled “”No authentication methods enabled””At least one auth method must be configured. Set AUTH_PASSWORD_ENABLED=true or configure an OAuth/OIDC provider.
Server refuses to start (“insecure default in production mode”)
Section titled “Server refuses to start (“insecure default in production mode”)”The instance detected production intent (an https:// URL, or INSECURE_DEV_MODE not true) while still using the template default CSRF_SECRET_KEY or ADMIN_BOOTSTRAP_PASSWORD. Set unique values for both — or, for local evaluation only, set INSECURE_DEV_MODE=true and use an http://localhost URL.
Cookies not persisting / login loop
Section titled “Cookies not persisting / login loop”Without HTTPS, you must set INSECURE_DEV_MODE=true. In production, use HTTPS and ensure INSECURE_DEV_MODE is false.
Database connection refused
Section titled “Database connection refused”- Verify
DATABASE_URLis correct and the Postgres server is reachable from the Docker network. - If using the bundled Postgres, ensure the
postgresservice is healthy:docker compose ps.
Port 8080 already in use
Section titled “Port 8080 already in use”Set PORT in .env to a free port — the published localhost port follows it automatically.