# ClassroomIO Docker Self-Hosting Environment # Copy this file to .env and fill in your values. # For local development, the defaults work out of the box. # For production, replace all placeholder values. # ────────────────────────────────────────────── # Image version (only used by docker-compose.images.yaml — the pull-based stack) # ────────────────────────────────────────────── # Which published image tag to pull for api/dashboard/jobs. # - "latest" (below) = freshest main build; it MOVES on every merge to main (rolling, may # include unreleased code), so a later `pull` can shift you to a newer build unannounced. # - For production, PIN an exact release (e.g. 1.4.2) so a merge to main never auto-breaks you. # "1.4.2" / "1.4" / "1" are immutable, published on a v* git tag. # Rollback = set this back to the previous version, then `... pull && up -d`. CIO_VERSION=1 # ────────────────────────────────────────────── # Required # ────────────────────────────────────────────── # Database (used by docker-compose to create the Postgres container) POSTGRES_DB=classroomio POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres # Auth secret for session signing. REQUIRED — the stack refuses to start if this is empty. BETTER_AUTH_SECRET= # Dashboard-to-API auth key (leave blank — auto-generated by ./run-docker-full-stack.sh). # Must be the same value in both API and dashboard. PRIVATE_SERVER_KEY= # Self-hosted mode flag. # Note: for the dashboard this is a BUILD-TIME value (baked into the client bundle via a # Docker build arg). The published dashboard image is built with PUBLIC_IS_SELFHOSTED=true; # changing it at runtime has no effect on an already-built image. PUBLIC_IS_SELFHOSTED=true # Dashboard SSR calls to the API inside the Docker network # Local: http://api:3081 | Production: http://api:3081 (always internal) PRIVATE_SERVER_URL=http://api:3081 # Dashboard origin for SvelteKit CSRF protection and invite/email links # Local: http://localhost:3082 | Production: https://app.yourdomain.com # When set, invite emails and other links point here instead of app.classroomio.com DASHBOARD_ORIGIN=http://localhost:3082 # Optional direct API origin. Dashboard browser calls are proxied through # DASHBOARD_ORIGIN, so normal self-hosted auth does not need this to share a # cookie domain with the dashboard. # Local: http://localhost:3081 | Production fallback: https://api.yourdomain.com PUBLIC_SERVER_URL=http://localhost:3081 # Optional comma-separated origins allowed when browsers call the API directly. # Normal self-hosted dashboard auth/API traffic is same-origin through the dashboard proxy. # DASHBOARD_ORIGIN is trusted automatically. TRUSTED_ORIGINS= # CSP: external domains allowed by Content-Security-Policy. # These are runtime env vars — set them when running the container (no rebuild needed). # Comma-separated. Include your CDN, video, and font domains. You do not need # to add the API here for normal dashboard traffic because API calls are proxied same-origin. # ALLOWED_EXTERNAL_DOMAINS overrides all per-directive vars below. ALLOWED_EXTERNAL_DOMAINS= # Per-directive overrides (optional, comma-separated): # CSP_SCRIPT_SRC_DOMAINS= # CSP_STYLE_SRC_DOMAINS= # CSP_CONNECT_SRC_DOMAINS= # CSP_FRAME_SRC_DOMAINS= # CSP_FONT_SRC_DOMAINS= # CSP_MEDIA_SRC_DOMAINS= (applies to both img-src and media-src) # ────────────────────────────────────────────── # Optional — Email (SMTP, or skip) # ────────────────────────────────────────────── # SMTP SMTP_HOST= SMTP_PORT= SMTP_USER= SMTP_PASSWORD= SMTP_SENDER= # ────────────────────────────────────────────── # Optional — Object Storage (auto-configured by ./run-docker-full-stack.sh) # ────────────────────────────────────────────── # MinIO is included by default. On first run ./run-docker-full-stack.sh RANDOMIZES the # MinIO credentials below and mirrors them into OBJECT_STORAGE_ACCESS_KEY_ID/SECRET. # These minioadmin placeholders are only used if you skip the script. MINIO_ROOT_USER=minioadmin MINIO_ROOT_PASSWORD=minioadmin OBJECT_STORAGE_ENDPOINT=http://minio:9000 OBJECT_STORAGE_PUBLIC_ENDPOINT=http://localhost:9000 OBJECT_STORAGE_ACCESS_KEY_ID=minioadmin OBJECT_STORAGE_SECRET_ACCESS_KEY=minioadmin OBJECT_STORAGE_FORCE_PATH_STYLE=true OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL=http://localhost:9000/media # ────────────────────────────────────────────── # Optional — Integrations # ────────────────────────────────────────────── # Google OAuth GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= # Dashboard: Google Drive video picker (optional). Web OAuth client ID + browser API key; enable Picker API and Drive API on the same GCP project. # PUBLIC_GOOGLE_PICKER_CLIENT_ID= # PUBLIC_GOOGLE_PICKER_API_KEY= # Dashboard domain routing PRIVATE_APP_HOST=localhost PRIVATE_APP_SUBDOMAINS=app # Unsplash image search UNSPLASH_API_KEY= # AI Assistant — set at least one provider key to enable the in-course chat. # The dashboard model picker exposes Gemini 3 Flash (Google) and GPT-4o (OpenAI). # Anthropic is supported in code but not surfaced in the picker UI. OPENAI_API_KEY= GOOGLE_API_KEY= ANTHROPIC_API_KEY= # Enterprise license (SSO, token-auth, no-tracking) LICENSE_KEY= # ────────────────────────────────────────────── # Optional — Upload file size limits (megabytes) # ────────────────────────────────────────────── # Unset = built-in defaults. Set on both api and dashboard (compose passes these through). # Also raise any reverse-proxy body-size limit (nginx client_max_body_size, etc.) if you go high. # UPLOAD_MAX_DOCUMENT_MB=5 # Course lesson documents (PDF, DOCX, DOC) # UPLOAD_MAX_IMAGE_MB=5 # Avatars, media library, editor images # UPLOAD_MAX_VIDEO_MB=800 # Lesson video uploads # UPLOAD_MAX_EXERCISE_FILE_MB=2 # Student assignment file-upload questions # UPLOAD_MAX_AGENT_DOCUMENT_MB=5 # AI assistant document uploads # UPLOAD_MAX_LANDING_IMAGE_MB=0.5 # Org landing page cover images # UPLOAD_MAX_THUMBNAIL_MB=5 # Media thumbnail uploads # Custom branding (meta tags) # PUBLIC_APP_TITLE=My Learning Platform # PUBLIC_APP_DESCRIPTION=A flexible LMS # PUBLIC_OG_IMAGE_URL=https://app.yourdomain.com/og-image.png