Files
classroomio/apps/docs/content/docs/self-hosted/configuration/secrets.mdx
T
Abdulmuizz Hamzat 0dc32b5210 refactor: selfhosting docs (#798)f
* Remove the run-docker-full-stack.sh script to streamline the deployment process and eliminate unnecessary complexity.

* fix: update documentation to clarify script behavior for building and pulling images

* update docs

* fix: update script URL to use classroomio.com for downloads

* fix: humanize selfhosting docs and minor amendment

* fix: attend to comment

* fix: humanize docs

* update docs

* fix: adjusted the Restarting ClassroomIO... function

* fix: update commands and documentation for environment variable handling and upgrades

* chore: added space for rebuild
2026-08-11 13:33:20 +01:00

77 lines
3.5 KiB
Plaintext

---
title: Environment & secrets
description: Generate the two secrets ClassroomIO needs to boot — BETTER_AUTH_SECRET and PRIVATE_SERVER_KEY.
---
ClassroomIO needs two secrets before it will run. Generate them once, keep them safe, and reuse the
same values across the services that need them.
## The two secrets
```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)
```
| Variable | What it's for | Where it goes |
|----------|---------------|---------------|
| `BETTER_AUTH_SECRET` | Signs authentication sessions. | The **api** service. |
| `PRIVATE_SERVER_KEY` | Authenticates server-side dashboard → API requests. | **Both** the api and dashboard; the value must be **identical** in each. |
:::danger[The stack won't start without them]
The compose file **fails fast** if `BETTER_AUTH_SECRET` or `PRIVATE_SERVER_KEY` is empty: the
containers refuse to boot rather than run with a predictable, guessable session-signing secret. Set
both to a strong random value (e.g. `openssl rand -hex 32`).
:::
:::note[Who generates them]
On Docker, `./classroomio.sh` auto-generates both secrets when they're missing (on install and
start, for the pull and build paths alike). Railway, Coolify, and Dokploy do **not** run that
script. There you generate the secrets yourself and paste them in.
:::
## Database credentials
Postgres runs inside the stack (or as a managed plugin on Railway). Set a strong password. The
defaults are for local use only.
| Variable | Description | Default |
|----------|-------------|---------|
| `POSTGRES_DB` | Database name | `classroomio` |
| `POSTGRES_USER` | Database user | `postgres` |
| `POSTGRES_PASSWORD` | Database password — set a **strong** value in production | `postgres` |
### Rotating `POSTGRES_PASSWORD` after install
:::danger[Editing `.env` alone doesn't rotate it]
The official Postgres image only reads `POSTGRES_PASSWORD` when it initializes a **fresh,
empty** data directory. Once `./classroomio.sh install` has run once, the data volume
already exists — changing `POSTGRES_PASSWORD` in `.env` afterward and restarting does
**not** change the database's actual password. It only changes the password `api`/`jobs`
*attempt* to connect with, so a mismatch breaks every DB connection.
To actually rotate it after install:
1. Set the new password inside Postgres itself:
```zsh title="Terminal"
docker compose --env-file .env -p classroomio -f docker-compose.images.yaml \
exec postgres psql -U postgres -c "ALTER ROLE postgres WITH PASSWORD '<new-password>';"
```
2. Update `POSTGRES_PASSWORD` in `.env` to the same value.
3. Run `./classroomio.sh restart` to recreate `api`/`jobs`/`postgres` with the matching value.
:::
## Mode & routing basics
| Variable | Description | Default |
|----------|-------------|---------|
| `PUBLIC_IS_SELFHOSTED` | Self-hosted mode flag | `true` |
| `PRIVATE_SERVER_URL` | Internal API URL for server-side rendering (over the container network) | `http://api:3081` |
| `DASHBOARD_ORIGIN` | Public dashboard origin, used as the auth base URL and in invite/email links | `http://localhost:3082` |
:::warning[Use an `https://` origin in production]
`DASHBOARD_ORIGIN` (and `ORIGIN` on the dashboard) must be the real `https://` URL your users visit:
auth session cookies are `Secure`, so login silently fails over plain HTTP on any public domain. See
[Self Hosting → HTTPS is required in production](/self-hosted#https-is-required-in-production).
:::