Installation
iSchool ships as production-ready Docker images via docker-compose.prod.yml, with one file, two deployment variants, selected by a Compose profile.
docker-compose.yml (no .prod) is the development stack — hardcoded credentials, an auto-seeded demo school, no TLS. Never use it for a real deployment. Use docker-compose.prod.yml instead.
1. Prepare your environment file
cp .env.example .envFill in, at minimum:
DB_PASSWORD— a real Postgres password.NEXT_PUBLIC_API_BASE_URL— the public URL the browser will reach the API at (e.g.https://api.yourschool.com).- Whichever provider keys you need live — see Getting Your API Keys. Everything degrades gracefully if left empty (a feature reports “not configured” instead of crashing), so you can start minimal and add keys later.
2. Choose your deployment variant
The SaaS variant includes the Platform Admin panel — the Brainers-Labs-only tool for onboarding schools, tracking payment and suspending/reactivating a school’s access.
docker compose -f docker-compose.prod.yml --profile saas up -d --buildAfter it’s up, bootstrap the very first platform-admin account (one-off, safe to re-run to rotate a password):
docker compose -f docker-compose.prod.yml run --rm seed-platform-admin(Or set PLATFORM_ADMIN_EMAIL/_PASSWORD/_NAME in .env and it runs automatically as part of up.)
Platform Admin is now reachable on port 3001. From there, onboard your first real school — there is no public self-registration page.
3. What’s running
| Service | Port | Notes |
|---|---|---|
api | 8080 | The Go core. Non-root container, health-checked. |
web | 3000 | The tenant dashboard every school’s staff use. |
platform-admin | 3001 | SaaS only. |
ai-sidecar | internal | Timetabling solver. No public port needed. |
gotenberg | internal | Real PDF rendering for report cards. |
postgres | internal | Add your own backup strategy — this compose file is not a backup solution. |
None of the application containers run as root. api, web, platform-admin and ai-sidecar all have Docker HEALTHCHECKs baked into their images.
4. TLS and a real domain
docker-compose.prod.yml does not terminate TLS itself — put a reverse proxy (Caddy, nginx, Traefik, or your cloud provider’s load balancer) in front of ports 8080/3000/3001 and point it at your real domain(s). The ALLOWED_ORIGINS env var on the API must match whatever origin(s) the browser actually loads the frontends from, or the API will reject cross-origin requests from them.
5. Real PDF report cards
Report cards render as PDF via Gotenberg, already included in the compose file as the gotenberg service. If GOTENBERG_URL is left unset, PDF requests degrade to an HTML response you can print from the browser instead of failing outright — but in a real deployment, leave it pointed at the bundled gotenberg service (the default in docker-compose.prod.yml) for real PDFs.