Self-Hosting Guide
RealmSSO runs entirely on your infrastructure. Here is what it needs from you.
What you need to run
- PostgreSQL 16
- Redis 7
- A Keycloak instance — either the one bundled for local dev, or your own
- Node.js 20 to run the server, or the provided Docker image
RealmSSO does not authenticate users itself. It orchestrates Keycloak — provisioning realms, identity providers, and OIDC clients — and your application then talks to Keycloak directly. Nothing about this changes if you self-host; RealmSSO simply becomes another service on your own infrastructure rather than a SaaS you point at.
The Keycloak privilege requirement
create-realm in the master realm is required
Every RealmSSO account gets its own Keycloak realm — the realm is the only boundary Keycloak actually enforces between tenants (see Accounts (Tenants)). Provisioning an account is creating a realm, and realm creation is inherently a master-realm operation in Keycloak, so the service account driving RealmSSO needs the create-realm role in master — a materially bigger ask of your security team than scoping it to one realm.
Per ADR 0001, this makes KEYCLOAK_ALLOW_REALM_CREATION=false — previously offered as a lower-privilege bring-your-own-Keycloak mode — incompatible with provisioning any new account. It is not a soft limitation to work around; it is a direct consequence of the tenant-isolation model.
If your security team cannot grant create-realm, set KEYCLOAK_ALLOW_REALM_CREATION=false and pre-create each account's realm out of band, named <KEYCLOAK_REALM>-<account slug>. RealmSSO adopts an existing realm rather than recreating it — but you are then responsible for creating every future account's realm yourself, before onboarding it, with the settings below.
Required service-account roles
| Role | Realm | Needed for |
|---|---|---|
| create-realm | master | Creating each account's own realm — the tenant boundary |
| view-realm | each managed realm | Startup and per-account realm existence checks |
| manage-realm | each managed realm | Applying a realm's default settings, and deleting an account's realm on teardown |
| manage-identity-providers | each managed realm | Creating and deleting SAML/OIDC identity providers |
| manage-clients | each managed realm | Creating and deleting an account's OIDC client |
| manage-users | each managed realm | Terminating brokered sessions and moving users during a realm migration |
A master-realm account with create-realm gets the per-realm roles implicitly. A least-privilege alternative is to pre-create each realm out of band and grant only the per-realm roles on each.
Bring your own Keycloak
RealmSSO can orchestrate an existing, independently-managed Keycloak instead of the bundled dev one. Realm bootstrap runs once at startup and does a single-realm read of your base KEYCLOAK_REALM — not a full listing of every realm on the instance — so it only needs visibility into its own realms, not the whole server.
If Keycloak is unreachable or under-privileged at boot, that failure is logged as a loud warning and the server starts anyway: /health and every route that does not touch Keycloak keep working, and only Keycloak-dependent routes (connections, account provisioning, SCIM) fail per-request until it is fixed. Never point two accounts at the same realm, and never use the base KEYCLOAK_REALM itself as a tenant realm.
Realms are adopted as-is, not reconfigured
When RealmSSO creates a realm, it sets security-relevant defaults on it — self-registration disabled, password reset disabled, bruteForceProtected on, and so on. When it finds a realm that already exists, it adopts it unchanged. If you pre-create a realm, those settings — plus the built-in first broker login flow and the default profile/email client scopes RealmSSO relies on for token claims — are yours to get right.
Building and running
docker/Dockerfile builds the production image — see Deployment Options for what its two build stages actually do. Building it directly:
docker build -f docker/Dockerfile -t realmsso-server .Apply schema migrations with prisma migrate deploy (wrapped by npm run db:migrate:prod) rather than migrate dev, which is interactive and meant for local development only.
Next steps
See Configuration for the full environment variable reference, and Deployment Options for running this in Docker Compose or Kubernetes.