Olympus Docs
CookbookMulti-tenant

Per-tenant config (multi-org in one deployment)

Serve multiple customer organizations from one Olympus deployment

Olympus is designed dual-domain (CIAM/IAM), not N-tenant. If you need separate identity domains for many customer organizations, you have three options.

Option 1: One Olympus deployment per tenant

The Olympus way. Each tenant gets:

  • Their own VPS.
  • Their own Olympus stack.
  • Complete isolation.

Pros: Clean isolation. Each tenant gets bespoke configuration. Compliance is straightforward.

Cons: Operational overhead × N. Cost × N.

Suitable for: enterprise B2B with <20 customers, each willing to pay.

Option 2: Tenant as a Kratos schema discriminator

One Olympus deployment, all tenants share Kratos. Identity schema includes a tenant_id trait:

"tenant_id": {
  "type": "string",
  "pattern": "^[a-z0-9-]+$",
  "title": "Organization"
}

Each registration assigns a tenant_id. Your apps filter by tenant_id on every query.

Pros: Cost-effective for many small tenants.

Cons: Single point of failure for all tenants. A bug in your filter logic leaks data across tenants. Not really isolated.

Suitable for: many small tenants in a B2C-ish product where data sharing risk is low.

Option 3: Tenant-per-CIAM-via-domain

Multiple subdomains, all proxied to the same Olympus deployment, but Hera renders per-tenant branding/config based on hostname.

acme.your-saas.com  → ciam-hera (with acme branding)
foo.your-saas.com   → ciam-hera (with foo branding)

Implementation: Hera reads Host header, looks up the tenant config from your settings vault.

const tenantId = req.headers.get("host")?.split(".")[0];
const tenantConfig = await getSetting("ciam", `tenant.${tenantId}.config`);
// Use tenantConfig.logo, tenantConfig.allowed_emails, etc.

Still one Olympus deployment, but per-tenant cosmetics.

What stays single-tenant in any option

  • The Olympus deployment itself.
  • Kratos and Hydra binaries.
  • Postgres (different databases per tenant if Option 1; shared otherwise).
  • Caddy.

Mixing options

Common pattern:

  • Option 1 for enterprise tier (compliance-sensitive customers).
  • Option 3 (per-domain branding) for self-service tier.
  • Option 2 rare, only when truly N independent identities matter.

Per-tenant OAuth2 clients

Even in Option 1, each tenant's OAuth2 clients are still managed via Athena → OAuth2 Clients. No "tenant" concept inside Hydra, just clients.

If you need per-tenant scope namespacing: prefix scopes with tenant ID (acme:read:widgets etc.).

Per-tenant settings

Use the settings vault with tenant-prefixed keys:

await getSetting("ciam", `tenant.${tenantId}.smtp.host`);
await getSetting("ciam", `tenant.${tenantId}.brand.logo_url`);

Wire your apps to read tenant-scoped settings.

Caveats

  • Cookie domain, if multiple tenant subdomains share your-saas.com as cookie domain, sessions accidentally span tenants.
  • OAuth2 clients with redirect URIs to wrong tenants, verify on registration.
  • Email "from" address, should match the tenant, not your generic SaaS.

On this page