Skip to content

Single-Tenant Apps

Most apps are not multi-tenant. If yours serves one organisation, you do not need to configure anything for tenancy — and you can skip the Multi-Tenancy guide entirely.

This page exists because the reassurance used to live inside that guide, which is the one page a single-tenant reader would never open.

Nothing. Multi-tenancy is off by default:

iam:
multi-tenancy:
enabled: false # the default — you do not need to write this

On first startup IAM seeds one tenant and one admin user, then registers the resources your @RequiresPermission annotations name. That is the whole tenancy story for your app.

Default Property
Tenant name Default iam.default-tenant.name
Tenant slug default derived from the name
Admin email admin@localhost iam.default-tenant.admin-email
Admin password (none — you set it) iam.default-tenant.admin-password

There is deliberately no built-in admin password. Set one from the environment, or run iam.dev-mode: true locally and IAM generates an ephemeral one and logs it at startup. A guessable literal would be a shipped backdoor.

iam:
default-tenant:
name: Acme # optional — cosmetic, and sets the slug
admin-password: ${IAM_ADMIN_PASSWORD} # required outside dev-mode

tenantSlug is optional. Omit it and IAM uses the default tenant:

Terminal window
curl -X POST localhost:8080/api/iam/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"admin@localhost","password":"..."}'

Everything the Multi-Tenancy guide describes is inert while enabled is false:

  • Tenant resolution — no X-Tenant-ID header, no subdomain parsing, no tid claim to plumb. Every request lands in the default tenant.
  • Isolation modes — row-level, schema and database are choices about separating tenants from each other. With one tenant there is nothing to separate.
  • Tenant provisioning — IamTenantProvisioning is for hosts that create tenants at runtime.

tenantId does not disappear from the API. IamUserDetails.tenantId is populated, admin endpoints are scoped to a tenant, and service methods take one. In a single-tenant app that value is simply always the same — the seeded default.

This is deliberate. It is what lets an app that starts single-tenant become multi-tenant later by flipping one flag, rather than by re-modelling its data. You can pass userDetails.tenantId through without thinking about it.

Set iam.multi-tenancy.enabled: true and choose how tenants are identified and separated. Your existing data stays where it is: the tenant it was seeded into becomes the first of several. See the Multi-Tenancy & Isolation guide.