ActiveManage Docs ← Back to activemanage.co.uk

Multitenancy

Overview of Multi-tenancy

Multi-tenancy lets a single ActiveManage install host multiple isolated customers ("tenants") on the same code and infrastructure. Each tenant sees their own data; admins of one tenant can't see another. This is the foundation for any SaaS product built on ActiveManage where you have customer organisations, each with their own users.

Multi-tenancy is opt-in — many ActiveManage installs are single-tenant and don't use the feature at all. For SaaS products, marketplaces, or any context where you're serving multiple independent customer organisations from one platform instance, it's essential.

Two isolation models

The Site Settings → Tenant → Tenant Database Mode picks between two ways of isolating tenant data:

Shared database mode

Every tenant's rows live in the same physical database, distinguished by a tenantid column on each row. The platform automatically filters every query by the current tenant — so users never see another tenant's data.

Cheap and easy to back up. One database to manage. One migration to run. The platform handles the tenant filtering automatically, so you don't have to worry about leaking data across tenants. Best for small-to-medium SaaS where every tenant doesn't need physical isolation.

Separate database mode

Each tenant gets their own physical MySQL database. The platform manages the connection per-tenant — when a user is in tenant 123, every query routes to that tenant's database; when they're in tenant 456, queries route to a different one.

Stronger isolation — a bug in tenant filtering can't accidentally leak data because there's nothing to filter (each tenant's data is in its own database). Needed for some compliance regimes that mandate physical isolation. More overhead to manage (more databases, more backups, more migrations).

When to use multi-tenancy

  • You're building a SaaS product with multiple customer organisations.
  • Different departments need fully isolated environments on shared infrastructure within a large enterprise.
  • You're a reseller / agency hosting multiple client apps on one platform install.
  • You need per-tenant customisation — branding, fields, workflows — without forking the codebase.

When NOT to use multi-tenancy

  • If your install serves just one organisation (yourself or one client), single-tenant is simpler.
  • If different parts of your business genuinely need different platform configurations (different schemas, different code), separate ActiveManage installs may be cleaner than multi-tenancy.
  • If you have very few tenants (say, 1-3) with high customisation per tenant, the multi-tenancy overhead may not be worth it.

Three real-world multi-tenancy setups

Example 1: B2B SaaS — many small tenants

100+ customer organisations, each with 5-50 users. Shared database mode. Tenants have isolated rows but share the same schema. Custom Tenant Information Fields capture per-tenant branding (logo, primary colour, support email). Per-tenant customisation is minimal — mostly just data isolation.

Example 2: Healthcare SaaS — fewer, larger tenants

20 customer organisations, each with 500-5000 users. Separate database mode. Stricter isolation needed for HIPAA-style compliance. Each tenant database backed up independently; tenant-specific data-residency requirements honoured by choice of database host (US-region tenant on US server, EU tenant on EU server, etc.).

Example 3: Multi-organisation enterprise app

5 internal business units acting as tenants. Shared database mode. Each business unit sees only their own customers/orders/invoices but reporting can roll up across all of them for executive dashboards (via custom queries that bypass the tenant filter).

Diagram showing the two multi-tenancy modes: shared database (one DB with tenantid column on every row) vs separate databases (one DB per tenant, platform routes queries per tenant context)

Shared vs Separate Tenant Databases

The Tenant Database Mode setting in Site Settings is one of the most consequential decisions you'll make in a multi-tenant install. Switching modes later is possible but painful, so think carefully up front.

Shared database mode

All tenants share the same physical database. Each datastore that supports multi-tenancy has a tenantid column; ownership of each row is determined by that column. The platform automatically filters every query by the current tenant's ID.

From the application's perspective, each tenant sees only their own rows. The filtering is transparent — your code doesn't need to add WHERE tenantid clauses; the platform injects them automatically on the read path and stamps tenantid on the write path.

Separate database mode

Each tenant gets their own physical database. The platform manages the connection per-tenant — when a user is in tenant 123, the platform connects to am_tenant_123; when they switch (in admin-tenant-switching scenarios) to tenant 456, it switches to am_tenant_456.

Tenant databases follow a naming convention (typically am_tenant_{id}). When a new tenant is created, the platform creates the new database and copies the schema from a template database.

Comparing the two modes

Cost and infrastructure

Shared: one database server, one backup pipeline, one migration to run. Cheaper to operate at any scale.

Separate: one database per tenant. More backups, more migrations, but each independent. Higher infrastructure cost.

Isolation

Shared: logical isolation only. A bug in your tenant filtering (rare but possible) could expose one tenant's data to another. The platform's automatic filtering is robust but custom code that bypasses it is a risk.

Separate: physical isolation. Even a serious bug can't cross databases. Easier to satisfy compliance regimes (HIPAA, GDPR data residency, financial-services data segregation).

Performance

Shared: one tenant with very heavy load can affect others ("noisy neighbour"). Mitigations: per-tenant rate limiting, query timeouts, monitoring.

Separate: each tenant's database is independent. Noisy neighbour isn't an issue at the database level. Larger tenants can be put on more powerful database hosts.

Backup and restore granularity

Shared: backups are per-database (one backup, many tenants). Restoring one tenant's data from backup is hard — you'd need to extract their rows from the backup and re-import.

Separate: per-tenant backups are trivial. "Restore tenant X's database" is one command.

Migration complexity

Shared: one migration runs for all tenants simultaneously. Fast to deploy.

Separate: migrations have to run on every tenant database. Slower; needs orchestration. More likely that one tenant's migration fails while others succeed, leaving the install in mixed state.

Picking

Pick shared when:

  • You're early-stage and want simplicity.
  • You have many small tenants (100+).
  • No compliance regime requires physical isolation.
  • You're confident in the platform's tenant-filtering.

Pick separate when:

  • You're in a regulated industry where compliance audits will examine isolation.
  • You have fewer, larger tenants (under 50, each with 500+ users).
  • Some tenants need data residency in specific regions.
  • You're a managed-service provider where customers expect dedicated databases.

Table comparing shared vs separate tenant database modes across infrastructure cost, isolation, performance, backup granularity, and migration complexity

Note: You can mix the modes — shared by default, with separate databases for premium tenants. The platform supports this via Tenant Sharding, where each tenant has a configured "database host" that can point at either the shared host or a dedicated host. Common pattern for SaaS that wants to offer enterprise-tier customers physical isolation as an upgrade.

Enabling Tenant Sharding

Tenant sharding distributes tenant databases across multiple database hosts — useful once a single MySQL server can no longer handle the load, or when you need regional database hosts for data-residency compliance.

When to shard

You'll know it's time when one of these is true:

  • You have hundreds of tenants and a single DB is slowing down — query latency rises, concurrent-connection counts hit limits, the DB host is CPU-bound.
  • Some tenants are dramatically larger than others and you want to isolate them — premium customers on dedicated database hosts, free-tier customers on shared infrastructure.
  • You want regional database hosts so EU tenants live on EU servers, US tenants on US servers, etc. — typically driven by GDPR-style data-residency requirements.
  • You're approaching the storage limits of a single MySQL server (millions of rows × hundreds of tenants × many years of history adds up).

Enabling sharding

Three steps:

1. Add the additional Database Hosts

From the Architect Panel → Database Hosts, add an entry for each additional MySQL/MariaDB server you want to use. Each host has its own hostname, port, credentials, and SSL settings.

Typical patterns:

  • Host 1: the original DB (typically remains the master tenant's host).
  • Host 2: a US-region DB for US tenants.
  • Host 3: an EU-region DB for EU tenants.
  • Hosts 4+: additional shards for capacity overflow.

2. Toggle Enable Tenant Sharding in Site Settings

Under Site Settings → Tenant. Once enabled, the platform routes each tenant's database connection to its configured host.

3. Assign tenants to hosts

Each tenant has a databasehost_id column on the .tenants table. Set this to the ID of the host the tenant should live on. New tenants get the default host (typically Host 1) unless you explicitly assign them elsewhere.

Moving existing tenants to a new shard

If you're sharding an existing install, you'll need to move some tenants off the original host. The general procedure:

  1. Take a backup of the tenant's database on the source host.
  2. Restore the backup to the target host.
  3. Update the tenant's databasehost_id in .tenants to point at the new host.
  4. Verify the tenant works correctly on the new host (test user logins, sample queries, etc.).
  5. After confirming, drop the database from the original host to free space.

Plan a maintenance window for this — there's no zero-downtime tenant migration built into the platform.

Operational considerations

Backups

Each host needs its own backup pipeline. The platform's own backup tools (if you use them) need to be configured per-host.

Migrations

Schema migrations now have to run on every host. Make sure your migration orchestration handles this — run on Host 1, verify, run on Host 2, etc.

Monitoring

Monitor each host independently. A capacity problem on Host 2 doesn't surface in Host 1's metrics.

Cross-host queries

The platform doesn't support cross-host JOINs. Any query that needs data from multiple tenants on different hosts has to fetch each side separately and join in PHP. Plan reports and dashboards accordingly.

Diagram showing tenant sharding: three database hosts (US-East, EU-West, Asia-Pacific), with tenants assigned to different hosts based on their region

Warning: Sharding is a one-way door for most installs. Once tenants are spread across multiple hosts, going back to a single-host setup means a big migration. Don't enable sharding speculatively; wait until you have a concrete reason and have done the capacity-planning work to confirm it's the right move.

Creating a Tenant for Each User

For SaaS apps where every new sign-up becomes its own organisation, enable Create Tenant for Each User in Site Settings → Tenant. This shifts the platform's behaviour from "one tenant per customer organisation" to "one tenant per signed-up user" — the right model for self-serve SaaS where individuals (not org admins) sign up.

The flow

When a new user is created under the master tenant (typically via self-registration), the platform also creates a new tenant for them. The flow:

  1. Anonymous visitor signs up via the self-registration form.
  2. The platform creates the user account in the master tenant initially.
  3. The platform creates a new tenant for them (with auto-generated ID).
  4. The user is moved into the new tenant.
  5. On first login, the user is prompted to provide a tenant name and tenant code.

From there, the user is the admin of their own tenant. They can add team members, configure tenant-specific settings, customise to taste.

When to disable

If you are planning to create a tenant within a custom script when a new user account is created (e.g. as part of a multi-step onboarding flow that creates the tenant, sets up initial data, and provisions the user), this setting must be disabled to prevent two tenants being created — one by the platform's default flow and another by your custom script.

The first-login tenant setup

The first time a self-registered user logs in to their freshly-created tenant, they see a setup form asking for:

  • Tenant Name — their organisation or workspace name (e.g. "Acme Corp", "Jane's Personal Workspace").
  • Tenant Code — a short slug used in URLs or internal references (e.g. "acme", "jane"). Often used as the subdomain in custom-domain setups.
  • Any custom Tenant Information Fields you've added.

Once they submit this, the tenant is fully configured and they land on their dashboard.

Tenant onboarding

The platform optionally copies template data into the new tenant — see the Copy Data to New Tenant setting. Useful for seeding the new tenant with example records, default lookup data, sample dashboards, etc. Makes the first-use experience feel less empty.

Three real-world configurations

Example 1: Project-management SaaS

New users sign up, get their own tenant, are prompted to name their workspace, then land on a dashboard with one example project pre-created. They can invite team members or work solo.

Example 2: B2C creator tool

New creators sign up, their workspace is named after their first name + last name initial by default. The platform skips the tenant-name setup form because it's auto-generated. They start using the tool immediately.

Example 3: Multi-step onboarding with payment

New users sign up, but the tenant isn't created immediately — they're shown a payment form first. A custom script then creates the tenant after successful payment. Create Tenant for Each User is disabled to prevent the default flow from creating a duplicate.

Screenshot of the first-login tenant setup form showing the Tenant Name and Tenant Code fields plus any custom tenant information fields configured

Tip: For self-serve SaaS, the smoothest onboarding has the smallest tenant-setup form. If you can derive a sensible default name from the user's data (e.g. their email's domain becomes the tenant name), skip the form entirely. Friction at this stage is a major drop-off point in conversion funnels.

Copying Data to a New Tenant

The Copy Data to New Tenant setting controls whether template data is automatically copied from the master tenant into each newly-created tenant. Done right, this makes new tenants feel populated and ready-to-use from the moment they're created instead of empty and intimidating.

What gets copied

Data from the master tenant for all datastores in the .tables table where appinitmode is 2 or 4 AND tenantshareddata is 0. That combination means:

  • The datastore is supposed to be initialised for new tenants (appinitmode > 0).
  • The datastore's data is per-tenant (tenantshareddata = 0), not shared across tenants.

For each matching datastore, every row from the master tenant is copied into the new tenant. The copy preserves field values but assigns new row IDs (since each tenant's database might have its own ID sequence in separate-database mode, and even in shared mode the rows are new logical records).

What doesn't get copied

  • Datastores with tenantshareddata = 1 — those are shared across tenants by reference, not copied.
  • Datastores with appinitmode = 0 or other modes that don't trigger auto-init.
  • User accounts — each tenant has its own users; nothing is copied.
  • Audit logs and history — clean slate for each new tenant.

Three common use cases

1. Pre-populating common lookup tables

Reference data like product categories, countries, currencies, status options. The master tenant has the canonical list; new tenants get a copy so they start with sensible defaults but can customise.

2. Loading template documents or email templates

Each tenant might need a starter set of document templates (invoices, quotes, contracts) and email templates (welcome, reset, notification). Set them up once on the master tenant; new tenants get their own copy that they can edit without affecting other tenants.

3. Seeding sample data for the first-run experience

Pre-populate example customers, sample orders, demo dashboards so the new tenant's UI doesn't look empty on first login. The user can delete the samples once they're ready to add real data.

Configuring which datastores copy

The decision per datastore is made via two fields on the .tables system row:

  • appinitmode
    • 0: don't auto-init on new tenant.
    • 1: create the table structure but no data.
    • 2: create structure + copy data from master.
    • 4: create structure + copy data + run any registered init scripts.
  • tenantshareddata — 0 for per-tenant data; 1 for shared data (one copy referenced by all tenants).

For each application-level datastore you create, decide which combination is right and configure accordingly.

Three real-world copy patterns

Pattern 1: Per-tenant with template seed data

A Customers datastore. appinitmode = 2 (copy from master), tenantshareddata = 0 (per-tenant data). New tenants start with whatever sample customers you've put in the master, but they can edit freely without affecting other tenants.

Pattern 2: Shared reference data

A Countries lookup datastore. appinitmode = 2, tenantshareddata = 1. Every tenant references the same Countries list. Changing the list affects everyone — usually that's what you want for reference data like this.

Pattern 3: Per-tenant empty

A Customer-specific datastore where each tenant fills it in themselves. appinitmode = 1 (create structure only), tenantshareddata = 0. New tenants get an empty table they can populate.

Screenshot of the .tables admin showing the appinitmode and tenantshareddata columns for several datastores, with the values reflecting different copy strategies

Tip: Don't go overboard with seed data. New tenants don't want to wade through ten sample customers and twenty sample orders just to find where to add their first real one. A handful of carefully-chosen examples (one or two per table) is enough to show what the platform can do without becoming clutter.