* Update documentation for ClassroomIO: change Fumadocs to Blume, revise MDX format conventions, and clarify sidebar navigation requirements. * docs: update README and scripts for improved preview and error handling * docs: update Discord link in configuration and documentation * docs: add token-auth route and update documentation formatting * Fix formatting in auto-comment-issues.yml * Fix JSON syntax by adding a comma * fix(docs): declare Blume's Astro build deps so Cloudflare build resolves them blume build generates an Astro project under apps/docs/.blume/ that imports astro, @astrojs/mdx, @tailwindcss/vite, tailwindcss, @tailwindcss/typography, @shikijs/twoslash and @scalar/astro. Under pnpm's isolated node_modules these are nested inside blume and not resolvable from the docs app, so a clean --frozen-lockfile build (Cloudflare) fails at 'Cannot find module astro/config'. Declare them directly in @cio/docs, pinned to blume's resolved versions. * refactor(docs): update API reference paths and documentation for clarity * fix: change platofrm url to docs home page * Refactor documentation for self-hosting ClassroomIO - Removed the Supabase Cloud setup guide as it is no longer needed. - Updated the Enterprise SSO documentation to include a reference link for SSO & licensing. - Added new documentation for Content Security Policy (CSP) configuration. - Created a new section for configuring SMTP for email notifications. - Introduced a comprehensive configuration index for self-hosted ClassroomIO, detailing environment variables and their usage. - Added documentation for optional integrations including Google OAuth, OpenAI, and Unsplash. - Documented the generation and usage of required secrets for ClassroomIO. - Created a section for SSO & licensing, detailing the requirements for Enterprise features. - Added object storage configuration documentation, covering both bundled MinIO and external S3-compatible services. - Documented versioning and upgrade processes for ClassroomIO. - Updated the Coolify and Docker self-hosting guides to streamline configuration references and upgrade instructions. - Enhanced the Railway self-hosting guide with links to relevant configuration sections. - Updated token authentication documentation to reference SSO & licensing. * Add comprehensive guides for ClassroomIO features and functionalities - Introduced a new guide for the Student Dashboard, detailing navigation and features available to students. - Added a guide on using Tags for organizing and filtering courses within ClassroomIO. - Created a guide on how to take attendance in courses, including marking attendance and related functionalities. - Developed a Terminology guide to clarify key terms used throughout ClassroomIO. - Implemented a Token Auth guide for signing users into ClassroomIO from external applications using JWT. - Added instructions for using math in the editor with LaTeX support. - Created a guide for customizing the welcome email sent to students upon enrollment in courses. - Added a guide explaining what ClassroomIO is and its key features. - Updated the home documentation to provide a clearer overview of ClassroomIO and its resources. - Enhanced self-hosted configuration documentation with a new guide for deploying ClassroomIO on Dokploy. - Updated links in the self-hosted configuration documentation for better navigation. - Added new images to support the Dokploy deployment guide. - Updated asset packaging script to handle redirects for the new guide structure. - Fixed footer link to point to the correct Getting Started guide. * chore: uprage version * feat(docs): add DigitalOcean deployment guide and flatten guides structure - Add a guide for deploying ClassroomIO on DigitalOcean using Docker Compose and Caddy for automatic HTTPS. - List DigitalOcean as a deployment option in the self-hosting index. - Move guides out of content/docs/guides into content/docs (student-dashboard, tags, terminology, token-auth, what-is-classroomio, audience, cohorts, community, course, enterprise-sso, mcp, organization, quickstart/*). - Rename home.mdx to index.mdx. - Update package-assets script to emit redirects for the old guides paths. --------- Co-authored-by: Rotimi Best <best@classroomio.com>
279 lines
15 KiB
Plaintext
279 lines
15 KiB
Plaintext
---
|
|
title: Coolify
|
|
description: Deploy ClassroomIO on Coolify using the pull-based Docker Compose stack, with a video walkthrough.
|
|
---
|
|
|
|
[Coolify](https://coolify.io) is a self-hosted PaaS (an open-source Heroku/Netlify alternative) you run on your own VPS. It runs ClassroomIO from the published `classroomio/*` images — your Coolify host **pulls** them and never builds anything.
|
|
|
|
## Watch the walkthrough
|
|
|
|
Prefer to follow along? This video walks through the whole Coolify deployment end to end. The written steps below mirror it, and the [Environment variable reference](#environment-variable-reference) has every value to copy.
|
|
|
|
{/* TODO: replace REPLACE_WITH_VIDEO_ID with the Coolify walkthrough video id once recorded */}
|
|
<YouTube id="5pwzUn8JBws" />
|
|
|
|
## Prerequisites
|
|
|
|
- A running **Coolify instance** with a connected server.
|
|
- A **domain** with a DNS **A record pointing at your Coolify server's IP**. Coolify's built-in proxy terminates HTTPS and issues the certificate for you.
|
|
- An **SMTP provider** (or Zoho/ZeptoMail) for signup verification, password reset, and invite emails. See [Nodemailer SMTP docs](https://nodemailer.com/smtp/).
|
|
- *Optional:* an **OpenAI API key** (transcription + AI course generation) and Google OAuth / Unsplash keys.
|
|
|
|
:::note[Two secrets to generate first]
|
|
You'll reuse `BETTER_AUTH_SECRET` and `PRIVATE_SERVER_KEY` across services. Coolify does **not**
|
|
run our secret-generation script, so generate them once yourself and keep them handy:
|
|
|
|
```zsh title="Terminal"
|
|
openssl rand -hex 32 # BETTER_AUTH_SECRET
|
|
openssl rand -hex 32 # PRIVATE_SERVER_KEY (must be identical in the api and dashboard services)
|
|
```
|
|
|
|
See [Configuration → Environment & secrets](/self-hosted/configuration/secrets) for what each does.
|
|
:::
|
|
|
|
## Step-by-step walkthrough
|
|
|
|
This uses a **Docker Compose resource**: you paste the compose file and set every environment value yourself. The flow is: **create a project → add the compose file → set environment variables → point the dashboard domain → deploy → sign up**.
|
|
|
|
<Steps>
|
|
|
|
<Step>
|
|
### Create a project and add a Docker Compose resource
|
|
|
|
Log in to Coolify and **create a project**. When you add a resource, Coolify asks what type it is — GitHub, a Docker image, a service, or a **Docker Compose** file. Choose **Docker Compose**, then **paste the contents of [`docker-compose.images.yaml`](https://github.com/classroomio/classroomio/blob/main/docker-compose.images.yaml)** into the editor.
|
|
|
|

|
|
|
|
</Step>
|
|
|
|
<Step>
|
|
### Confirm the services parsed
|
|
|
|
After saving, Coolify parses the compose file and lists the services: `postgres`, `redis`, `api`, `dashboard`, `jobs` (and `minio` once you enable its profile in the next step). Confirm they all appear before moving on.
|
|

|
|

|
|
</Step>
|
|
|
|
<Step>
|
|
### Set the environment variables
|
|
|
|
Open the resource's **Environment Variables** page. This is where most Coolify deployments go wrong, so treat the [Environment variable reference](#environment-variable-reference) below as the source of truth: **set every variable it lists as required, and leave the optional ones blank.** The block here covers the essentials to get you started; the full copy-paste reference is a few sections down.
|
|
|
|
```zsh title="Essentials — set all of these"
|
|
COMPOSE_PROFILES=minio # runs the bundled MinIO object storage
|
|
DASHBOARD_ORIGIN=https://dash.yourdomain.com
|
|
BETTER_AUTH_SECRET=<openssl rand -hex 32>
|
|
PRIVATE_SERVER_KEY=<openssl rand -hex 32> # must be identical in the api and dashboard services
|
|
POSTGRES_PASSWORD=<strong password>
|
|
# Email (signup verification, password reset, invites)
|
|
SMTP_HOST=smtp.yourprovider.com
|
|
SMTP_PORT=587
|
|
SMTP_USER=<smtp username>
|
|
SMTP_PASSWORD=<smtp password>
|
|
SMTP_SENDER=ClassroomIO <you@yourdomain.com>
|
|
# Object storage + domains: see the full reference below
|
|
```
|
|
|
|
👉 The essentials above get you started, but the complete list of what to set — object storage, domains, and every optional variable — is in the [**Environment variable reference**](#environment-variable-reference) below. Copy that full block before you deploy.
|
|
|
|
:::note[How to read the reference: value = required, blank = optional]
|
|
There's no way to guess which variables Coolify needs, so the [Environment variable
|
|
reference](#environment-variable-reference) spells it out. The rule is simple: **every variable
|
|
shown with a value must be set** for a smooth deployment (secrets, database, domains, object
|
|
storage, and SMTP are all needed). **Every variable left blank after `=` is optional** — fill it in
|
|
only if you use that integration. Leaving an optional one blank never causes a build or boot error.
|
|
:::
|
|
|
|

|
|
</Step>
|
|
|
|
<Step>
|
|
### Confirm the image tag
|
|
|
|
`CIO_VERSION` chooses the tag pulled for `classroomio/{api,dashboard,jobs}`. **Confirm the tag you use actually exists** at [hub.docker.com/r/classroomio](https://hub.docker.com/r/classroomio/) before deploying.
|
|
|
|
:::note[Pin a released version]
|
|
Set `CIO_VERSION=1.0.0` to pin an exact release. `latest` is the freshest `main` build — it moves
|
|
on every merge (rolling; may include unreleased code), while the version tags (`1.0.0`, `1.0`, `1`)
|
|
are immutable, pinned releases. Pinning an exact release makes upgrades deliberate and deploys
|
|
reproducible.
|
|
:::
|
|
|
|

|
|
</Step>
|
|
|
|
<Step>
|
|
### Set the dashboard domain
|
|
|
|
Open the **dashboard** service → **Settings** and set its domain, including the internal port, e.g. `https://dash.yourdomain.com:3082`. Coolify maps that FQDN to the container's port `3082` and issues the certificate through its proxy.
|
|
|
|
The dashboard proxies API calls server-side, so you normally only expose the dashboard publicly — the `api` service stays internal.
|
|
|
|

|
|
</Step>
|
|
|
|
<Step>
|
|
### Deploy
|
|
|
|
Click **Deploy**. Coolify pulls `classroomio/{api,dashboard,jobs}:${CIO_VERSION}`, starts Postgres + Redis, the API container runs database setup on first boot, then the dashboard and the **jobs worker** come up. Watch the **logs** — they tell you when every service is up.
|
|
|
|

|
|
</Step>
|
|
|
|
<Step>
|
|
### Open the app and sign up
|
|
|
|
Once the logs show the stack is healthy, open your dashboard URL (`https://dash.yourdomain.com`) in a new browser tab and **sign up**. The **first** account becomes the organization **admin**. Every signup or sign-in after that joins as a student — unless you invite them with a specific role.
|
|
|
|

|
|
</Step>
|
|
|
|
</Steps>
|
|
|
|
## Environment variable reference
|
|
|
|
You set these on the resource's Environment Variables page (Step 3). This is the complete picture — copy the block below, then replace the placeholder values with your own.
|
|
|
|
:::note[Value = required, blank = optional]
|
|
**Every variable shown with a value below is required** for a working deployment. **Every variable
|
|
left blank after `=` is optional** — fill it in only if you use that feature. A blank optional
|
|
variable never causes a build or boot error, so don't put junk into fields you aren't using.
|
|
:::
|
|
|
|
```zsh title=".env — a complete working Coolify deployment"
|
|
# ── Secrets (generate with: openssl rand -hex 32) ──
|
|
BETTER_AUTH_SECRET=<openssl rand -hex 32>
|
|
PRIVATE_SERVER_KEY=<openssl rand -hex 32> # identical in the api and dashboard services
|
|
|
|
# ── Object storage profile ──
|
|
COMPOSE_PROFILES=minio # runs the bundled MinIO alongside the stack
|
|
|
|
# ── Database ──
|
|
POSTGRES_DB=classroomio
|
|
POSTGRES_USER=postgres
|
|
POSTGRES_PASSWORD=<strong password>
|
|
|
|
# ── Domains & routing ──
|
|
PUBLIC_IS_SELFHOSTED=true
|
|
DASHBOARD_ORIGIN=https://dash.yourdomain.com
|
|
PUBLIC_SERVER_URL=https://dash.yourdomain.com
|
|
PRIVATE_SERVER_URL=http://api:3081
|
|
PRIVATE_APP_HOST=dash.yourdomain.com
|
|
PRIVATE_APP_SUBDOMAINS=app
|
|
|
|
# ── Object storage (MinIO) ──
|
|
MINIO_ROOT_USER=<storage access key>
|
|
MINIO_ROOT_PASSWORD=<storage secret>
|
|
MINIO_SERVER_URL=https://storage.yourdomain.com
|
|
OBJECT_STORAGE_ENDPOINT=http://minio:9000
|
|
OBJECT_STORAGE_PUBLIC_ENDPOINT=https://storage.yourdomain.com
|
|
OBJECT_STORAGE_ACCESS_KEY_ID=<same as MINIO_ROOT_USER>
|
|
OBJECT_STORAGE_SECRET_ACCESS_KEY=<same as MINIO_ROOT_PASSWORD>
|
|
OBJECT_STORAGE_FORCE_PATH_STYLE=true
|
|
OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL=https://storage.yourdomain.com/media
|
|
OBJECT_STORAGE_BUCKET_VIDEOS=videos
|
|
OBJECT_STORAGE_BUCKET_DOCUMENTS=documents
|
|
OBJECT_STORAGE_BUCKET_MEDIA=media
|
|
CSP_MEDIA_SRC_DOMAINS=https://storage.yourdomain.com # so browsers can load media
|
|
|
|
# ── Email (SMTP — signup verification, password reset, invites) ──
|
|
SMTP_HOST=smtp.yourprovider.com
|
|
SMTP_PORT=587
|
|
SMTP_USER=<smtp username>
|
|
SMTP_PASSWORD=<smtp password>
|
|
SMTP_SENDER=ClassroomIO <you@yourdomain.com>
|
|
|
|
# ── Optional — leave blank unless you need them ──
|
|
CIO_VERSION=1.0.0 # pinned release; blank = latest (rolling main build)
|
|
GOOGLE_CLIENT_ID= # Google OAuth sign-in
|
|
GOOGLE_CLIENT_SECRET=
|
|
OPENAI_API_KEY= # transcription + AI course generation
|
|
UNSPLASH_API_KEY= # Unsplash image search
|
|
LICENSE_KEY= # enterprise features (SSO)
|
|
ALLOWED_EXTERNAL_DOMAINS= # overrides all CSP_*_DOMAINS below
|
|
CSP_SCRIPT_SRC_DOMAINS=
|
|
CSP_STYLE_SRC_DOMAINS=
|
|
CSP_CONNECT_SRC_DOMAINS=
|
|
CSP_FRAME_SRC_DOMAINS=
|
|
CSP_FONT_SRC_DOMAINS=
|
|
TRUSTED_ORIGINS= # only for direct browser-to-API calls (proxy is same-origin)
|
|
OBJECT_STORAGE_REGION= # only for some external S3 providers
|
|
SKIP_DB_SETUP= # blank = run DB setup on first boot (leave blank)
|
|
```
|
|
|
|
### Required — set all of these
|
|
|
|
| Variable | What it's for |
|
|
|----------|---------------|
|
|
| `BETTER_AUTH_SECRET` | Session-signing secret. Generate with `openssl rand -hex 32`. |
|
|
| `PRIVATE_SERVER_KEY` | Dashboard-to-API auth key. Must be **identical** in the api and dashboard services. |
|
|
| `COMPOSE_PROFILES` | Set to `minio` to run the bundled object storage with the stack. |
|
|
| `POSTGRES_DB` / `POSTGRES_USER` / `POSTGRES_PASSWORD` | Database name, user, and a **strong** password. |
|
|
| `PUBLIC_IS_SELFHOSTED` | `true` for self-hosted mode. |
|
|
| `DASHBOARD_ORIGIN` | Public dashboard URL — used as the auth base URL and in invite/email links. |
|
|
| `PUBLIC_SERVER_URL` | Public origin the browser uses (your dashboard domain). |
|
|
| `PRIVATE_SERVER_URL` | Internal API URL over the Docker network (`http://api:3081`). |
|
|
| `PRIVATE_APP_HOST` / `PRIVATE_APP_SUBDOMAINS` | Dashboard host and subdomain routing. |
|
|
| `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` | MinIO credentials — also used as the object-storage access key / secret. |
|
|
| `MINIO_SERVER_URL` | Public storage URL (fixes MinIO console redirects behind the proxy). |
|
|
| `OBJECT_STORAGE_ENDPOINT` | Internal endpoint the app uploads to (`http://minio:9000`). |
|
|
| `OBJECT_STORAGE_PUBLIC_ENDPOINT` | Public storage endpoint browsers download from. |
|
|
| `OBJECT_STORAGE_ACCESS_KEY_ID` / `OBJECT_STORAGE_SECRET_ACCESS_KEY` | Storage credentials (match the MinIO root user/password). |
|
|
| `OBJECT_STORAGE_FORCE_PATH_STYLE` | `true` — required for MinIO. |
|
|
| `OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL` | Public base URL for media (`…/media`). |
|
|
| `OBJECT_STORAGE_BUCKET_VIDEOS/DOCUMENTS/MEDIA` | Bucket names (`videos`, `documents`, `media`). |
|
|
| `CSP_MEDIA_SRC_DOMAINS` | Your storage domain — otherwise the browser's CSP blocks uploaded media. |
|
|
| `SMTP_HOST` / `SMTP_PORT` / `SMTP_USER` / `SMTP_PASSWORD` / `SMTP_SENDER` | SMTP server for signup verification, password reset, and invites. |
|
|
|
|
:::warning[If the stack won't start at all]
|
|
Of the above, `BETTER_AUTH_SECRET` and `PRIVATE_SERVER_KEY` are the two the compose file
|
|
**fails fast** on: leave either empty and the containers refuse to boot (so ClassroomIO never runs
|
|
with a predictable session-signing secret). The rest won't abort the boot, but the deployment
|
|
won't work correctly without them — set them all.
|
|
:::
|
|
|
|
### Optional — leave blank unless you need them
|
|
|
|
| Variable | Enables |
|
|
|----------|---------|
|
|
| `CIO_VERSION` | Image tag to pull. Blank = `latest` (rolling main); pin an exact release such as `1.0.0` (see the tag step). |
|
|
| `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` | Google OAuth sign-in. |
|
|
| `OPENAI_API_KEY` | Transcription + AI course generation (jobs worker). |
|
|
| `UNSPLASH_API_KEY` | Unsplash image search. |
|
|
| `LICENSE_KEY` | Enterprise features — SSO requires a license ([book a call](http://cal.com/classroomio/enterprise)). |
|
|
| `ALLOWED_EXTERNAL_DOMAINS` | Overrides all `CSP_*_DOMAINS` vars with one comma-separated allowlist. |
|
|
| `CSP_SCRIPT_SRC_DOMAINS`, `CSP_STYLE_SRC_DOMAINS`, `CSP_CONNECT_SRC_DOMAINS`, `CSP_FRAME_SRC_DOMAINS`, `CSP_FONT_SRC_DOMAINS` | Per-directive CSP allowlists for extra CDN/font/frame domains. |
|
|
| `TRUSTED_ORIGINS` | Extra origins allowed to call the API directly (normal dashboard traffic is same-origin via the proxy). |
|
|
| `OBJECT_STORAGE_REGION` | Region for some external S3 providers (not needed for MinIO). |
|
|
| `SKIP_DB_SETUP` | Skips first-boot database setup — leave blank so it runs. |
|
|
| `PUBLIC_APP_TITLE`, `PUBLIC_APP_DESCRIPTION`, `PUBLIC_OG_IMAGE_URL` | Custom branding / meta tags. |
|
|
|
|
### Object storage
|
|
|
|
Enable the **bundled MinIO** with `COMPOSE_PROFILES=minio` and Coolify runs it alongside the stack.
|
|
**Give the MinIO volume persistent storage in Coolify** and route its public URL
|
|
(`OBJECT_STORAGE_PUBLIC_ENDPOINT` / `OBJECT_STORAGE_MEDIA_PUBLIC_BASE_URL`) so browsers can load media.
|
|
|
|
:::warning[MinIO is your durability boundary]
|
|
Single-node MinIO has no redundancy — its volume is the only thing protecting your uploads, so give it
|
|
persistent storage in Coolify and back it up.
|
|
:::
|
|
|
|
Prefer managed storage? Point the `OBJECT_STORAGE_*` variables at external S3 / Cloudflare R2 and leave
|
|
the `minio` profile off. See [Configuration → Object storage & uploads](/self-hosted/configuration/storage).
|
|
|
|
### Upload size limits (optional)
|
|
|
|
Set `UPLOAD_MAX_*_MB` in Coolify's env for both the `api` and `dashboard` services (same values in
|
|
each), e.g. `UPLOAD_MAX_DOCUMENT_MB=50`. See
|
|
[Configuration → Upload file size limits](/self-hosted/configuration/storage#upload-file-size-limits).
|
|
|
|
## Upgrades & rollback
|
|
|
|
Bump `CIO_VERSION` to a newer release and redeploy; roll back by setting it to the previous version and redeploying. Because you pin a version, a new release never restarts you onto broken images unattended. See [Configuration → Versions & upgrades](/self-hosted/configuration/versions) for the full upgrade and backup steps.
|
|
|
|
:::warning[Back up before upgrading]
|
|
Schema migrations run automatically on API startup. Back up your Postgres (and object storage)
|
|
before a major upgrade.
|
|
:::
|