Files
classroomio/docker/docs/SELF_HOST.md
T
Abdulmuizz Hamzat 5e2dcf71f4 docs: add dev setup guide and make package scripts cross-platform (Windows) (#664)
* chore: update README with environment setup instructions and demo account credentials

fix: replace rm -rf with rimraf in build scripts for cross-platform compatibility

fix: add shell option to pnpm command in db-setup script for Windows compatibility

chore: update package.json files to use rimraf instead of rm -rf for consistency

chore: add rimraf dependency to pnpm-lock.yaml

feat: add postinstall script to install lefthook git hooks in a cross-platform manner

* docs(qa): add QA test plan, flow checklists, and bug-report workflow

Add a repo-native QA execution layer on top of FEATURE_AUDIT.md (the
feature map). Test cases live in the repo; bugs live in GitHub Issues, so
there is no duplicate tracker to keep in sync.

QA system:
- FEATURE_AUDIT.md: the feature map - every feature, its user flow, and
  edge cases with file:line citations.
- qa/README.md: the workflow, severity/difficulty rubrics, label setup,
  and a static feature -> flow coverage map proving all 42 audited
  features have a test plan.
- qa/flows/01..13: per-flow happy-path checklists covering signup &
  onboarding, course authoring, people/invites/roles, student ->
  certificate, AI assistant & tutor, programs/attendance, community,
  org public site, media & import, org settings, public API/MCP,
  enterprise SSO/licensing, and billing & AI credits.
- .github/ISSUE_TEMPLATE/qa-bug.yml: a QA Bug template requiring
  severity, difficulty, area, repro steps, and a screen recording.

Dev tooling:
- package.json: add db:studio and docker:build scripts.
- packages/db: add db:studio (drizzle-kit studio); fix db:reset on
  Windows by spawning the pnpm.cmd shim with shell:true (Node's
  CVE-2024-27980 fix blocks spawning .cmd/.bat without a shell).

* chore: update QA bug template and README for clarity and improved workflow

* docs: enhance DEV_SETUP_NOTES and README for clarity on prerequisites and email setup
chore: update package.json scripts for cross-platform compatibility
feat: add cross-platform Prettier check script for pre-commit hook

* Remove QA documentation and flow files for ClassroomIO

- Deleted the entire QA README.md file which contained the QA process, bug filing guidelines, and feature coverage map.
- Removed all flow files (01 to 13) that detailed the QA testing processes for various features including admin signup, course authoring, people management, student learning, AI assistant, programs, community, org public site, media management, org settings, public API, enterprise SSO, billing, and AI credits.

* fix: update postinstall script
2026-06-15 17:40:06 +01:00

6.4 KiB

ClassroomIO Docker Self-Hosting

Prerequisites

  • Docker Desktop installed and running
  • git installed

Quick Start

git clone https://github.com/classroomio/classroomio.git
cd classroomio
cp .env.example .env
# Edit .env — set your dashboard domain and secrets
./run-docker-full-stack.sh

The script builds and starts: postgres, redis, minio (object storage), api (localhost:3081), and dashboard (localhost:3082). The api container runs database schema setup on startup (skip with SKIP_DB_SETUP=true).

It auto-generates secure values for AUTH_BEARER_TOKEN and PRIVATE_SERVER_KEY when they are missing.

Environment Variables

All Docker services read from a single root .env file. See .env.example for the full annotated template with required/optional grouping.

Key points:

  • Required: BETTER_AUTH_SECRET, PUBLIC_IS_SELFHOSTED=true, DASHBOARD_ORIGIN.
  • API routing: Browser dashboard calls go to the dashboard origin and are proxied to PRIVATE_SERVER_URL (default: http://api:3081). You do not need API/dashboard cookie-domain matching for normal dashboard auth.
  • Direct API access (optional): Set PUBLIC_SERVER_URL and TRUSTED_ORIGINS only if browsers or third-party clients need to call the API origin directly.
  • CSP (runtime): ALLOWED_EXTERNAL_DOMAINS (overrides all) or per-directive: CSP_SCRIPT_SRC_DOMAINS, CSP_STYLE_SRC_DOMAINS, CSP_CONNECT_SRC_DOMAINS, CSP_FRAME_SRC_DOMAINS, CSP_FONT_SRC_DOMAINS, CSP_MEDIA_SRC_DOMAINS. These are read at container startup — no image rebuild needed. The API does not need to be added for normal dashboard calls.
  • Auth cookies: No cookie-domain env is needed. The dashboard proxy makes auth first-party and Better Auth sets host-only dashboard cookies.
  • Auto-generated: AUTH_BEARER_TOKEN, PRIVATE_SERVER_KEY (by ./run-docker-full-stack.sh).
  • Auto-configured: All MINIO_* / OBJECT_STORAGE_* vars (by the startup script).
  • Optional: Email (SMTP or Zoho), Google OAuth, Unsplash, LICENSE_KEY (enterprise).

PRIVATE_SERVER_KEY must be the same value in both API and dashboard — the script ensures this.

Verify

docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml ps
curl -sS http://localhost:3081/    # API — returns JSON
curl -I  http://localhost:3082/    # Dashboard — returns 200

Seed Demo Data

docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml \
  run --rm --entrypoint sh api -c "pnpm --filter @cio/db db:setup -- --seed"

Command Reference

All commands use this prefix — abbreviated as dc below:

dc = docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml
Task Command
Full stack (with MinIO) ./run-docker-full-stack.sh
Skip image rebuild ./run-docker-full-stack.sh --no-build
Exclude MinIO ./run-docker-full-stack.sh --no-minio
API-only smoke test dc up --build -d postgres redis api
Service status dc ps
Stream logs dc logs -f api dashboard
Rebuild one service dc up -d --build api
Restart (env change, no rebuild) dc restart api dashboard
Stop dc down
Stop + delete volumes dc down -v

Build Images Locally

docker build -f docker/Dockerfile.api -t classroomio/api:latest .
docker build -f docker/Dockerfile.dashboard -t classroomio/dashboard:latest .

Inspect Container Env Vars

docker exec cio-api env | sort
docker exec cio-dashboard env | sort

Container names: cio-postgres, cio-redis, cio-api, cio-dashboard, cio-minio.

MinIO (Object Storage)

MinIO is included by default. The startup script auto-configures env vars, starts MinIO, and creates videos, documents, and media buckets.

  • API endpoint: http://localhost:9000
  • Web UI: http://localhost:9001 (default: minioadmin / minioadmin)

To start MinIO manually (e.g. after --no-minio):

docker compose -f docker/docker-compose.yaml --profile minio up -d

Security: Change MINIO_ROOT_USER / MINIO_ROOT_PASSWORD in .env for production. Use the same values for OBJECT_STORAGE_ACCESS_KEY_ID / OBJECT_STORAGE_SECRET_ACCESS_KEY.

MinIO Web UI Not Loading

If port 9001 does not respond, recreate the container:

docker rm -f cio-minio
docker compose -f docker/docker-compose.yaml --profile minio up -d

Data is preserved in the minio-data volume.

Rebuild After Code Changes

This setup builds images from source (no live hot reload). Code changes require a rebuild:

# One service
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up -d --build api

# Both (e.g. shared packages/* changed)
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up -d --build api dashboard

# Full stack
./run-docker-full-stack.sh

Troubleshooting

"No space left on device"

df -h                          # check host disk
docker system df               # check Docker usage
docker builder prune -f        # free build cache
docker image prune -a -f       # free unused images
docker container prune -f      # free stopped containers

Warning: docker volume prune removes volumes not used by any container. If run after docker compose ... down, it deletes postgres-data, redis-data, and minio-data. Use only the prunes above to free space safely.

Then restart: ./run-docker-full-stack.sh

"The database system is in recovery mode" (57P03)

Postgres has not finished starting. Wait 30-60 seconds and retry. The compose healthcheck requires a successful SELECT 1 before dependent services start.

MinIO "Storage reached its minimum free drive threshold"

Same root cause as disk full. Free space using the steps above and restart MinIO:

docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml restart minio

SMTP Errors in Logs

ECONNREFUSED 127.0.0.1:465 means SMTP is not configured. This does not block startup — configure SMTP or Zoho when you need email.

Port Conflicts

lsof -nP -iTCP:3081 -sTCP:LISTEN
lsof -nP -iTCP:3082 -sTCP:LISTEN