* 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
6.4 KiB
ClassroomIO Docker Self-Hosting
Prerequisites
- Docker Desktop installed and running
gitinstalled
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_URLandTRUSTED_ORIGINSonly 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 pruneremoves volumes not used by any container. If run afterdocker compose ... down, it deletespostgres-data,redis-data, andminio-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
Related
- Self-hosting docs — published guide with Railway + Docker instructions
docker/docs/PUBLISHING_IMAGES.md— publishing Docker images to Docker Hub