docs: selfhostin improvements

This commit is contained in:
Rotimi Best
2026-03-11 10:53:03 +01:00
parent d19dddd8cd
commit a98d6a35fa
11 changed files with 410 additions and 1226 deletions
+1 -32
View File
@@ -102,38 +102,7 @@ docker pull classroomio/dashboard:latest
### Deploy with Compose
Use the production compose file (`docker/docker-compose.prod.yaml`) with pinned image tags.
1. Copy the production env template:
```bash
cp docker/.env.prod.example docker/.env.prod
```
2. Set pinned images in `docker/.env.prod`:
```bash
CIO_API_IMAGE=classroomio/api:v0.1.0
CIO_DASHBOARD_IMAGE=classroomio/dashboard:v0.1.0
```
3. Deploy:
```bash
docker compose -p classroomio --env-file docker/.env.prod -f docker/docker-compose.prod.yaml pull
docker compose -p classroomio --env-file docker/.env.prod -f docker/docker-compose.prod.yaml up -d
```
The `db-init` service also uses `CIO_API_IMAGE` so schema setup always matches the API build you deploy.
Important URL behavior for dashboard API calls:
- Browser requests use `PUBLIC_SERVER_URL` (set this to your public API domain, not `localhost`).
- Server-side dashboard requests use `PRIVATE_SERVER_URL` when set, with fallback to `PUBLIC_SERVER_URL`.
- In Docker network deployments, set `PRIVATE_SERVER_URL=http://api:3081`.
- Set `AUTH_COOKIE_DOMAIN` to `your-domain.com` (shared subdomain cookies) or your exact API host if you need explicit Better Auth cookie domain control.
For local source builds, keep using `docker/docker-compose.yaml`.
For self-hosting with Docker Compose, see [SELF_HOST.md](SELF_HOST.md). The setup uses a single root `.env` file and `docker/docker-compose.yaml`.
## Versioning Strategy
+119 -198
View File
@@ -1,249 +1,170 @@
# ClassroomIO Docker Selfhosting Setup
# ClassroomIO Docker Self-Hosting
This guide is for someone setting up ClassroomIO with Docker for the first time.
## 1. Prerequisites
## Prerequisites
- Docker Desktop installed and running
- `git` installed
- `curl` installed (recommended for health checks)
## 2. Clone the Repo
## Quick Start
```bash
git clone --branch feat/course-v2 https://github.com/classroomio/classroomio.git
git clone https://github.com/classroomio/classroomio.git
cd classroomio
```
## 3. Create Root `.env` for Docker Compose
Create `./.env` in the repository root:
```bash
cat > .env <<'EOF'
# Database
POSTGRES_DB=classroomio
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
# API auth/secrets
# Leave blank to auto-generate on first ./run-docker-full-stack.sh
AUTH_BEARER_TOKEN=
BETTER_AUTH_SECRET=replace-with-a-long-random-secret
# Dashboard server-to-server auth
# Leave blank to auto-generate on first ./run-docker-full-stack.sh
PRIVATE_SERVER_KEY=
# Enterprise features (optional: SSO, token-auth, no-tracking)
# Request a key from ClassroomIO. Leave empty if you do not use Enterprise-only features.
LICENSE_KEY=
# API and dashboard URLs
PUBLIC_SERVER_URL=http://localhost:3081
# Dashboard server-side calls (inside Docker network)
PRIVATE_SERVER_URL=http://api:3081
TRUSTED_ORIGINS=http://localhost:3082,http://localhost:5173
AUTH_COOKIE_DOMAIN=
PUBLIC_IS_SELFHOSTED=true
# Dashboard domain config
PRIVATE_APP_HOST=domain.com
PRIVATE_APP_SUBDOMAINS=app,launchweek
# CSP: External domains (required when PUBLIC_IS_SELFHOSTED=true; no defaults)
# Passed as build args to the dashboard. Rebuild dashboard after changing: --build dashboard
# Comma-separated list for Content-Security-Policy. Include your CDN, API, and video domains.
# Example (adjust to your instance):
ALLOWED_EXTERNAL_DOMAINS=https://*.classroomio.com,https://api.classroomio.com,https://pgrest.classroomio.com,wss://*.classroomio.com,https://assets.cdn.clsrio.com,https://cdn.plyr.io
# Email delivery (choose one approach)
# Option A: Zoho/ZeptoMail token
ZOHO_TOKEN=
# Option B: SMTP
SMTP_HOST=
SMTP_PORT=
SMTP_USER=
SMTP_PASSWORD=
SMTP_SENDER=
# Object storage - MinIO (included by default; script auto-sets these if missing)
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
EOF
```
Important:
- `./run-docker-full-stack.sh` auto-generates secure values for `AUTH_BEARER_TOKEN` and `PRIVATE_SERVER_KEY` when missing/insecure.
- `./run-docker-full-stack.sh` always uses root `.env` (`docker compose --env-file .env ...`) for compose build/runtime variables.
- API key middleware for dashboard/server-to-server calls validates `PRIVATE_SERVER_KEY`.
- `AUTH_BEARER_TOKEN` is separate and is not used by `apiKeyMiddleware`.
- Enterprise-only features (SSO, token-auth, no-tracking) require `LICENSE_KEY`. If you do not need those features, leave `LICENSE_KEY` empty.
- Dashboard API URL behavior is split by environment: browser requests use `PUBLIC_SERVER_URL`, and server-side dashboard requests use `PRIVATE_SERVER_URL` (fallback: `PUBLIC_SERVER_URL`).
- In hosted deployments, `PUBLIC_SERVER_URL` must be your public API domain (not `localhost`), while Docker internal SSR calls should use `PRIVATE_SERVER_URL=http://api:3081`.
- `AUTH_COOKIE_DOMAIN` is optional. Leave it empty for Better Auth defaults, or set it to `.your-domain.com` (shared cookies across subdomains) or your API host domain when you need explicit cookie domain control.
- Do not use invalid domains (for example `b.com` or `.com` when your backend is `api.b.com`); use `.b.com` or the exact backend host.
- For email sending, configure either `ZOHO_TOKEN` or a full SMTP config (`SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_SENDER`).
- If neither Zoho token nor SMTP is configured, emails will not send.
- Keep real secrets out of version control.
- When `PUBLIC_IS_SELFHOSTED=true`, the dashboard CSP uses **only** env vars—no defaults. Set `ALLOWED_EXTERNAL_DOMAINS` with all external domains your instance needs (CDNs, video providers, analytics, etc.).
- **Object storage:** MinIO is included by default. The script auto-sets MinIO env vars if missing. Use `--no-minio` to exclude object storage (video/document/media uploads will not work).
## 4. Start Full Docker Stack
From repo root:
```bash
cp .env.example .env
# Edit .env — set your domain, secrets, and ALLOWED_EXTERNAL_DOMAINS
./run-docker-full-stack.sh
# Skip image rebuild and only start containers
./run-docker-full-stack.sh --no-build
# Exclude MinIO (video/document/media uploads will not work)
./run-docker-full-stack.sh --no-minio
```
This script:
The script builds and starts: **postgres**, **redis**, **minio** (object storage), **db-init** (schema setup), **api** (`localhost:3081`), and **dashboard** (`localhost:3082`).
- builds and starts `postgres`, `redis`, `db-init`, `api`, `dashboard`, and **MinIO** (object storage)
- auto-configures MinIO env vars (if missing), runs `minio-init` (creates videos, documents, media buckets)
- prints compose status
- checks API (`3081`) and dashboard (`3082`) endpoints
It auto-generates secure values for `AUTH_BEARER_TOKEN` and `PRIVATE_SERVER_KEY` when they are missing.
MinIO Web UI: `http://localhost:9001` (default: `minioadmin` / `minioadmin`).
## Environment Variables
## 5. Verify Manually (Optional)
All Docker services read from a single root `.env` file. See [`.env.example`](../../.env.example) for the full annotated template with required/optional grouping.
Key points:
- **Required:** `PUBLIC_SERVER_URL`, `TRUSTED_ORIGINS`, `BETTER_AUTH_SECRET`, `PUBLIC_IS_SELFHOSTED=true`, `ALLOWED_EXTERNAL_DOMAINS`, `DASHBOARD_ORIGIN`.
- **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
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml ps
curl -sS http://localhost:3081/
curl -I http://localhost:3082/
curl -sS http://localhost:3081/ # API — returns JSON
curl -I http://localhost:3082/ # Dashboard — returns 200
```
## 6. Access
- API: `http://localhost:3081`
- Dashboard: `http://localhost:3082`
## 7. Trigger Seed Data
To run DB setup and seed demo data in one command:
## Seed Demo Data
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml run --rm db-init sh -c "pnpm --filter @cio/db db:setup -- --seed"
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml \
run --rm db-init sh -c "pnpm --filter @cio/db db:setup -- --seed"
```
Seed only (skip DB setup):
## Command Reference
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml run --rm db-init sh -c "pnpm --filter @cio/db seed"
All commands use this prefix — abbreviated as `dc` below:
```
dc = docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml
```
Use seed-only only after setup has already run at least once (`db:setup`, which now creates roles and syncs schema).
| 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 db-init 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` |
`--rm` removes the one-off `db-init` container after the command finishes so you do not accumulate stopped seed containers.
## 8. Restart After Source Code Changes
Because this Docker setup builds images from source (no live bind-mount hot reload), code changes require a rebuild.
Rebuild and restart only one service:
### Build Images Locally
```bash
docker build -f docker/Dockerfile.api -t classroomio/api:latest .
docker build -f docker/Dockerfile.dashboard -t classroomio/dashboard:latest .
```
### Inspect Container Env Vars
```bash
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`):
```bash
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:
```bash
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:
```bash
# One service
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up -d --build api
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up -d --build dashboard
```
If you changed shared code used by both (`packages/*`), rebuild both:
```bash
# Both (e.g. shared packages/* changed)
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up -d --build api dashboard
```
Rebuild full stack:
```bash
# Full stack
./run-docker-full-stack.sh
```
If you only changed environment variables (no code change), restart without rebuild:
## Troubleshooting
### "No space left on device"
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml restart api dashboard
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
```
## 9. Stop
> **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:
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml down
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml restart minio
```
## 10. If PostgreSQL or Docker Fails with "No space left on device"
### SMTP Errors in Logs
The error `could not write lock file "postmaster.pid": No space left on device` means the disk (or Docker storage) is full. Fix it by freeing space, then restart Postgres.
`ECONNREFUSED 127.0.0.1:465` means SMTP is not configured. This does not block startup — configure SMTP or Zoho when you need email.
**1. Check disk usage**
### Port Conflicts
```bash
df -h
docker system df
lsof -nP -iTCP:3081 -sTCP:LISTEN
lsof -nP -iTCP:3082 -sTCP:LISTEN
```
**2. Free Docker space (safe — removes unused build cache and images)**
## Related
```bash
docker builder prune -f
docker image prune -a -f
docker container prune -f
```
**3. If still low on space, remove unused volumes** (only if you do not need data in other projects’ volumes)
```bash
docker volume prune -f
```
Warning: `docker volume prune` removes volumes not used by any container. If you run it after `docker compose ... down`, it can remove `postgres-data`, `redis-data`, and `minio-data`, and you will lose database and object storage data. To free space without touching Classroom.io data, avoid `down` and use only the prunes in step 2, or remove specific unused volumes by name.
**4. Restart the stack**
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up -d
```
If the host disk is still full after pruning, free space elsewhere (e.g. large files, old logs, other apps) or increase disk capacity.
## 11. "The database system is in recovery mode" (57P03)
This happens when the API (or another service) hits Postgres before it has finished starting or replaying WAL after a crash/restart. Our Postgres healthcheck now requires a successful `SELECT 1` (not just `pg_isready`) so dependent services only start once the DB accepts queries.
If you still see it (e.g. old compose file or a very slow disk):
1. Wait 30–60 seconds and retry the request.
2. Ensure you’re using the latest `docker/docker-compose.yaml` (healthcheck runs `pg_isready` and `psql ... SELECT 1`).
3. Restart the stack so Postgres gets a clean start and the API waits for the updated healthcheck: `./run-docker-full-stack.sh`.
## 12. MinIO: "Storage reached its minimum free drive threshold"
MinIO’s data scanner reports this when the disk (or the volume backing `minio-data`) is below MinIO’s minimum free space. Same root cause as [§10](#10-if-postgresql-or-docker-fails-with-no-space-left-on-device): the host or Docker storage is too full.
**What to do:**
1. Free space using the same steps as in **§10** (e.g. `docker system df`, `docker builder prune -f`, `docker image prune -a -f`, `docker container prune -f`). Avoid `docker volume prune` if you need to keep existing data.
2. After freeing space, restart MinIO (or the full stack) so the scanner sees the new free space:
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml restart minio
```
Or restart everything: `./run-docker-full-stack.sh`.
3. If the disk is still near full, remove unneeded objects from MinIO buckets (via Console or `mc`) or add/attach more storage.
MinIO does not expose an env var to lower or disable this threshold; freeing space or adding capacity is the intended fix.
- [Self-hosting docs](https://classroomio.com/docs/quickstart/self-hosting) — published guide with Railway + Docker instructions
- [`docker/docs/PUBLISHING_IMAGES.md`](PUBLISHING_IMAGES.md) — publishing Docker images to Docker Hub
-247
View File
@@ -1,247 +0,0 @@
# Docker Usage Guide
This directory contains Docker files for local self-hosting and image publishing.
## Stack Overview
`docker/docker-compose.yaml` starts these services:
- `postgres`: primary database used by `@cio/db` and `@cio/api`
- `redis`: cache/rate-limit store used by `@cio/api`
- `db-init`: one-off setup job (`pnpm --filter @cio/db db:setup`, which creates required roles and syncs schema)
- `api`: backend service on `http://localhost:3081`
- `dashboard`: frontend service on `http://localhost:3082`
For published-image deployments, use `docker/docker-compose.prod.yaml` (same services, but `api`, `dashboard`, and `db-init` run from published images instead of local builds).
Notes:
- Postgres and Redis are internal-only in compose (not exposed on host ports by default).
- `db-init` is expected to exit with code `0` after setup.
- If you run seed-only commands, make sure setup has already run once so schema exists.
## Quick Start
Run from the repository root:
```bash
# Full stack with MinIO (object storage) - default
./run-docker-full-stack.sh
# Skip image rebuild and only start containers
./run-docker-full-stack.sh --no-build
# Exclude MinIO (video/document/media uploads will not work)
./run-docker-full-stack.sh --no-minio
```
Verify:
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml ps
curl -sS http://localhost:3081/
curl -I http://localhost:3082/
```
Expected:
- API returns welcome JSON
- Dashboard returns `HTTP/1.1 200 OK`
## API-Only Smoke Test
If you only want to validate API startup (recommended first check):
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up --build -d postgres redis db-init api
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml ps
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml logs --tail=100 api
curl -sS http://localhost:3081/
```
## Production (Published Images)
Create a production env file from the template:
```bash
cp docker/.env.prod.example docker/.env.prod
```
Set pinned image tags in `docker/.env.prod`:
```bash
CIO_API_IMAGE=classroomio/api:v0.1.0
CIO_DASHBOARD_IMAGE=classroomio/dashboard:v0.1.0
# Optional: Enterprise-only features (SSO, token-auth, no-tracking)
# LICENSE_KEY=your-issued-license-key
```
Then deploy:
```bash
docker compose -p classroomio --env-file docker/.env.prod -f docker/docker-compose.prod.yaml pull
docker compose -p classroomio --env-file docker/.env.prod -f docker/docker-compose.prod.yaml up -d
```
## Environment Variables
Create a root `.env` file for compose. For local Docker, you can leave the token values blank and the startup script will generate secure values and keep them synchronized:
```bash
POSTGRES_DB=classroomio
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
BETTER_AUTH_SECRET=replace-with-a-strong-secret
AUTH_BEARER_TOKEN=
PRIVATE_SERVER_KEY=
# Optional: required only for Enterprise-only features (SSO, token-auth, no-tracking)
LICENSE_KEY=
PUBLIC_SERVER_URL=https://api.your-domain.com
PRIVATE_SERVER_URL=http://api:3081
TRUSTED_ORIGINS=https://app.your-domain.com
AUTH_COOKIE_DOMAIN=
```
Optional integrations (if used):
```bash
SMTP_HOST=
SMTP_PORT=
SMTP_USER=
SMTP_PASSWORD=
SMTP_SENDER=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
UNSPLASH_API_KEY=
```
Object storage (MinIO included by default; script auto-sets these):
```bash
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
```
Important:
- `./run-docker-full-stack.sh` auto-generates secure `AUTH_BEARER_TOKEN`/`PRIVATE_SERVER_KEY` values when missing or insecure placeholders are used.
- `./run-docker-full-stack.sh` always uses root `.env` (`docker compose --env-file .env ...`) so compose build/runtime variables come from repository-root `.env`.
- API key middleware for dashboard/server-to-server calls validates `PRIVATE_SERVER_KEY`.
- `AUTH_BEARER_TOKEN` is a separate token and is not used by `apiKeyMiddleware`.
- Enterprise-only features (SSO, token-auth, no-tracking) require `LICENSE_KEY`. If you do not use those features, leave it unset.
- Dashboard URL selection is environment-aware: browser calls use `PUBLIC_SERVER_URL`, while SSR/server-side calls use `PRIVATE_SERVER_URL` (fallback: `PUBLIC_SERVER_URL`).
- In Docker, set `PRIVATE_SERVER_URL=http://api:3081` for internal service-to-service traffic, and keep `PUBLIC_SERVER_URL` as your public API URL (do not use `localhost` in hosted deployments).
- `AUTH_COOKIE_DOMAIN` is required for setting cookies. Set it to `.your-domain.com` for cross-subdomain cookies, or to your exact API host when you need host-scoped cookies. Leave empty to use Better Auth defaults.
## Common Commands
```bash
# Start / rebuild
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up --build -d
# Stream logs
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml logs -f api dashboard
# Stop
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml down
# Stop and remove volumes (deletes local DB/cache data)
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml down -v
```
### API or dashboard fails to start
```bash
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml logs --tail=200 api dashboard db-init postgres redis
```
### SMTP errors in API logs
If SMTP is not configured, logs may include `ECONNREFUSED 127.0.0.1:465`. This does not block API startup.
### Port conflicts
```bash
lsof -nP -iTCP:3081 -sTCP:LISTEN
lsof -nP -iTCP:3082 -sTCP:LISTEN
```
## MinIO (S3-compatible storage)
MinIO is **included by default** when you run `./run-docker-full-stack.sh`. The script auto-configures env vars and starts MinIO + minio-init (creates videos, documents, media buckets).
To start MinIO manually (e.g. if you used `--no-minio` previously):
```bash
docker compose -f docker/docker-compose.yaml --profile minio up -d
```
### Access URLs
| Purpose | URL | Notes |
|---------|-----|-------|
| API | `http://localhost:9000` | Used by the ClassroomIO API for S3-compatible storage |
| Web UI | `http://localhost:9001` | Login to browse buckets, manage objects (default: `minioadmin` / `minioadmin`) |
> **Security:** Change the default credentials (`minioadmin` / `minioadmin`) in production. Set `MINIO_ROOT_USER` and `MINIO_ROOT_PASSWORD` in `.env`, and use the same values for `OBJECT_STORAGE_ACCESS_KEY_ID` and `OBJECT_STORAGE_SECRET_ACCESS_KEY`.
### Environment variables
Set in `.env`:
```bash
# MinIO container credentials (must match OBJECT_STORAGE_* below)
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin
# When API runs in Docker: use minio (Docker service name)
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
```
> **API in Docker:** Use `OBJECT_STORAGE_ENDPOINT=http://minio:9000` so the API container can reach MinIO on the Docker network. Use `OBJECT_STORAGE_ENDPOINT=http://localhost:9000` when the API runs locally (e.g. `pnpm api:dev`).
`MINIO_ROOT_USER` and `MINIO_ROOT_PASSWORD` configure the MinIO container. Set them to the same values as `OBJECT_STORAGE_ACCESS_KEY_ID` and `OBJECT_STORAGE_SECRET_ACCESS_KEY` when using the built-in MinIO service.
### MinIO troubleshooting
#### Web UI not loading
MinIO assigns a **random port** for the Web Console by default. If the Web UI at `http://localhost:9001` does not load, your container may have been created before we pinned the console port.
**Fix:** Remove the old container and recreate it:
```bash
docker rm -f cio-minio
docker compose -f docker/docker-compose.yaml --profile minio up -d
```
Our `docker-compose.yaml` uses `--console-address ":9001"` so the Web UI consistently listens on port 9001. Your data is stored in the `minio-data` volume and is preserved across container recreation.
#### Container name conflict
If you see `The container name "/cio-minio" is already in use`, remove the existing container first:
```bash
docker rm -f cio-minio
docker compose -f docker/docker-compose.yaml --profile minio up -d
```
## Related Docs
- `docker/docs/commands.md` for quick command snippets
- `docker/docs/PUBLISHING_IMAGES.md` for Docker Hub publishing
-177
View File
@@ -1,177 +0,0 @@
# Quick Docker Commands Reference
## Compose Stack
```bash
# Full stack with MinIO (default; generates/syncs secure auth tokens)
./run-docker-full-stack.sh
# Full stack without rebuilding images
./run-docker-full-stack.sh --no-build
# Exclude MinIO (object storage)
./run-docker-full-stack.sh --no-minio
# Full stack (postgres, redis, db-init, api, dashboard, minio)
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml --profile minio up --build -d
# API-only smoke test path (plus required dependencies)
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml up --build -d postgres redis db-init api
# Service status
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml ps
# Logs
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml logs -f api
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml logs -f dashboard
# Stop services
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml down
# Stop + remove volumes (deletes local postgres/redis data)
docker compose --env-file .env -p classroomio -f docker/docker-compose.yaml down -v
```
## MinIO (S3-compatible storage)
MinIO is **included by default** with `./run-docker-full-stack.sh`. To start MinIO manually:
```bash
docker compose -f docker/docker-compose.yaml --profile minio up -d
# If Web UI doesn't load or container name conflict:
docker rm -f cio-minio
docker compose -f docker/docker-compose.yaml --profile minio up -d
```
**When API runs in Docker:** Use `OBJECT_STORAGE_ENDPOINT=http://minio:9000` (Docker service name). The script auto-sets this when MinIO is included.
**Security:** Change the default `minioadmin` / `minioadmin` credentials in production. Set `MINIO_ROOT_USER` and `MINIO_ROOT_PASSWORD` in `.env` (and matching `OBJECT_STORAGE_*` vars).
See `docker/docs/USAGE.md` for MinIO env vars and troubleshooting.
## Compose Stack (Published Images / Production)
```bash
# 1) Copy and edit production env values (including pinned image tags)
cp docker/.env.prod.example docker/.env.prod
# 2) Pull pinned images
docker compose -p classroomio --env-file docker/.env.prod -f docker/docker-compose.prod.yaml pull
# 3) Start stack from published images
docker compose -p classroomio --env-file docker/.env.prod -f docker/docker-compose.prod.yaml up -d
# 4) Inspect status and logs
docker compose -p classroomio --env-file docker/.env.prod -f docker/docker-compose.prod.yaml ps
docker compose -p classroomio --env-file docker/.env.prod -f docker/docker-compose.prod.yaml logs -f api dashboard
```
## Health Checks
```bash
# API should return welcome JSON
curl -sS http://localhost:3081/
# Dashboard should return 200
curl -I http://localhost:3082/
```
## Build Images Locally
```bash
docker build -f docker/Dockerfile.api -t classroomio/api:latest .
docker build -f docker/Dockerfile.dashboard -t classroomio/dashboard:latest .
```
## Push to Docker Hub
```bash
docker push classroomio/api:latest
docker push classroomio/dashboard:latest
```
## All-in-One Publish Script
```bash
# Default username=classroomio version=latest
./docker/docker-push.sh
# Custom username
DOCKERHUB_USERNAME=your-username ./docker/docker-push.sh
# Versioned tag
VERSION=v1.0.0 ./docker/docker-push.sh
```
## Pull Published Images
```bash
docker pull classroomio/api:latest
docker pull classroomio/dashboard:latest
```
## Run Published Images (Manual)
Use compose for local development. If you run manually, include required runtime env.
`LICENSE_KEY` is only needed if you use Enterprise-only features.
```bash
# API
docker run -d --name cio-api -p 3081:3081 \
-e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/classroomio \
-e PRIVATE_DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/classroomio \
-e REDIS_URL=redis://host.docker.internal:6379 \
-e PUBLIC_SERVER_URL=https://api.your-domain.com \
-e TRUSTED_ORIGINS=https://app.your-domain.com \
-e AUTH_COOKIE_DOMAIN=.your-domain.com \
-e BETTER_AUTH_SECRET=<strong-random-secret> \
-e PRIVATE_SERVER_KEY=<dashboard-server-api-key> \
-e AUTH_BEARER_TOKEN=<optional-other-bearer-token> \
-e LICENSE_KEY=<your-issued-license-key> \
classroomio/api:latest
# Dashboard
docker run -d --name cio-dashboard -p 3082:3082 \
-e PUBLIC_SERVER_URL=http://localhost:3081 \
-e PRIVATE_SERVER_KEY=<dashboard-server-api-key> \
-e PUBLIC_IS_SELFHOSTED=true \
classroomio/dashboard:latest
```
## Inspect environment variables in a container
```bash
# List all env vars in a running container (use actual container name)
docker exec <container_name> env
# Sorted, one per line
docker exec <container_name> env | sort
# Inspect from host (env as configured at container create time)
docker inspect <container_name> --format '{{range .Config.Env}}{{println .}}{{end}}'
# Print a single variable
docker exec <container_name> printenv DATABASE_URL
```
**Container names** (from `docker compose ... ps`): `cio-postgres`, `cio-redis`, `cio-api`, `cio-dashboard`; with MinIO: `cio-minio`, `cio-minio-init`.
Example:
```bash
docker exec cio-api env | sort
```
## Useful Commands
```bash
# List local images
docker images | grep classroomio
# Remove local images
docker rmi classroomio/api:latest classroomio/dashboard:latest
# Inspect image
docker inspect classroomio/api:latest
```