Multi-tenancy
Dirigible runs in multi-tenant mode by default (DIRIGIBLE_MULTI_TENANT_MODE=true). A single platform instance serves multiple tenants from the same JVM, with per-tenant isolation for the artefact types that own user data.
Tenant resolution
Which tenant a request belongs to is decided by DIRIGIBLE_TENANT_RESOLUTION_STRATEGY, and there are two answers.
By host (SUBDOMAIN, the default). The request's host header is matched against DIRIGIBLE_TENANT_SUBDOMAIN_REGEX, and the first capture group is the tenant subdomain. With a regex like ^([a-z0-9-]+)\.example\.com$, requests to acme.example.com resolve to tenant acme. Every tenant needs a host of its own.
By identity (TOKEN_GROUPS). One host serves every tenant, and the tenant is the one the signed-in user selected. What a user may select is carried by their identity provider groups, named <tenantId>.<appId>.<role>, so the identity provider remains the single place where access is granted and the host says nothing about the tenant. See /help/setup/multi-tenancy for how to configure either one.
The current tenant is exposed through TenantContext (components/core/core-base/.../tenant/). User code reaches it via @aerokit/sdk/security (JS / TS) or by injecting TenantContext into a Java service.
What is tenant-isolated
These artefact types reconcile per tenant, via MultitenantBaseSynchronizer:
- Data sources (
.datasource) - each tenant gets its own JDBC pool registration. - CSV import / export (
.csvim,.csv) - tenant-scoped data loads. - OData services (
.odata) - per-tenant CXF surface. - Documents (CMS storage) - the internal CMIS root, S3 prefix, and SharePoint site are tenant-keyed.
- Scheduled jobs (
.job) - each tenant's Quartz triggers fire independently. - Listeners (
.listener) - per-tenant ActiveMQ consumers.
In practice this means tenant A and tenant B can both author the same .datasource file in their project tree and end up with different runtime data sources.
What is system-level
These artefact types are not tenant-isolated - they reconcile once for the whole platform:
- BPMN process instances (
.bpmn) - one Flowable engine; processes are global. - Camel integration flows (
.camel) - one Camel context. - Declared extensions (
.extensionpoint,.extension) - shared extension registry. - Git perspective - workspace-level operations.
- Development workspaces (
/users/<user>/workspace/...) - user-keyed, not tenant-keyed.
When you need tenant awareness inside a system-level artefact, read TenantContext at runtime instead of relying on synchronizer-level isolation.
How the runtime keeps tenants apart
- Data sources.
DataSourcesManager.getDefaultDataSource()returns a tenant-resolved view of the configured pool; SQL emitted from any language hits the tenant's DB. - Synchronizer iteration.
MultitenantBaseSynchronizerwalks the artefact set once per known tenant. - Thread context.
ThreadContextFacadepropagates the current tenant across request handlers, scheduled jobs, and message listeners.
Switching modes off
DIRIGIBLE_MULTI_TENANT_MODE=false collapses everything to a single default tenant. Useful for single-tenant deployments, where tenant resolution is irrelevant whichever strategy is configured.
Reference
Tenant,TenantContext,TenantResult<T>-components/core/core-base/.../tenant/MultitenantBaseSynchronizer-components/core/core-base/.../synchronizer/TenantProvisioningStep,TenantPostProvisioningStep- onboarding hooks for new tenantsDIRIGIBLE_TENANT_RESOLUTION_STRATEGY-SUBDOMAINorTOKEN_GROUPSDIRIGIBLE_TENANTS_PROVISIONING_FREQUENCY_SECONDS- tenant provisioning cadence