Installation
OpenRegistro supports two storage backends behind the same application: a JSON file driver that needs no database, and PostgreSQL for production deployments. Pick one at install time through the setup wizard - switching later requires the JSON-to-Postgres migration script (see Backups).
Option A: no database (JSON driver)
Good for trying the app, a single small school, or local development.
pnpm install
pnpm dev:json
This starts the app on http://localhost:3000 with STORAGE_DRIVER=json and data written
to ./data. Open the URL in a browser - you will land on the first-run setup wizard.
For a containerized version of the same thing:
cp apps/web/.env.example .env # set AUTH_SECRET at minimum
docker compose -f docker-compose.json.yml up
Option B: PostgreSQL
Recommended for any deployment with more than a handful of users.
cp apps/web/.env.example .env # set AUTH_SECRET at minimum
docker compose up
On the very first run against a fresh Postgres volume, apply migrations once before using
the app (see the comment at the top of docker-compose.yml for the exact command - the
runtime image is a minimal standalone build and does not include the migration tooling, so
this runs from your host machine with pnpm installed).
The setup wizard
Whichever option you choose, the first visit to the app walks through:
- Welcome (
/setup/welcome) - a Node.js/environment sanity check. - Storage driver (
/setup/driver) - confirms the configured driver can connect/write. - School details (
/setup/school) - school name and the current school year's dates. - Administrator account (
/setup/admin) - creates the firstschool_adminuser. - SMTP (
/setup/smtp) - optional; can be skipped and configured later from the app's settings (announcements and password-reset email just won't send until this is set). - Demo data (
/setup/demo) - optionally loads a fictional demo dataset (seeapps/web/lib/seed-demo.ts- every record uses a@demo.openregistro.localaddress, no real person's data).
Once this flow completes, the installer is permanently locked - visiting /setup/* again
redirects straight to the dashboard (or to /login if not signed in). There is currently no
UI path to re-run the wizard; if you need to start over, see Backups for how
to reset storage.
Reverse proxy / HTTPS
The app does not terminate TLS itself. Put it behind a reverse proxy (Caddy, nginx, Traefik)
that handles HTTPS and forwards to the container's port 3000, and set AUTH_URL to the
public HTTPS URL so Auth.js generates correct redirect/callback URLs.
Found an issue with this page? Edit it on GitHub.