Skip to main content
Yosavo Studio by Youssef Ibaichou , home
Expertise

Multi-Tenant SaaS

Multi-tenancy affects far more than database structure. It changes authentication, authorization, billing, reporting, background jobs, storage and operational tooling across the entire product.

Data isolation · Tenant context · Membership · Noisy neighbours

How I think about it

How I think about multi-tenancy

The most important question is not “how do we add a tenant_id?” It is where tenant boundaries exist, how they are enforced consistently, and what happens when data flows through APIs, jobs, files, reports and integrations.

  • Tenant context is resolved once and carried everywhere — explicitly.
  • Scoping is automatic by default; bypassing it is explicit and audited.
  • A cross-tenant leak is a security incident, so isolation gets its own tests.
  • Plan for the largest customer being a thousand times bigger than the smallest.

Key experience

Where this comes from

  • Led development of a multi-tenant, multi-user file collaboration platform — architecture, backend, database design, APIs and frontend integration.
  • SaaS architecture and delivery across products where organizations, roles and permissions are core concerns.

Scope

What I know

  • Tenant hierarchy design
  • Tenant identification
  • Shared vs schema vs database isolation
  • Automatic query scoping
  • Membership & per-tenant roles
  • Tenant-level billing
  • Tenant-aware jobs
  • Tenant-scoped storage
  • Cross-tenant reporting
  • Support & admin access
  • Tenant-aware caching & search
  • Isolation testing

Deep dive

Multi-Tenant SaaS in practice

On this page (14 topics)

What is a tenant?

The tenant is the unit that owns data and pays — usually an organization or customer account, sometimes a workspace, team or business unit inside one. Getting this wrong is expensive: if the product treats a team as the tenant and a customer later needs several teams under one contract and one invoice, billing, permissions and reporting all need rework.

So the first design question is the hierarchy: who pays, who owns the data, and who can see across which boundaries.

Tenant identification

A request can identify its tenant through a subdomain (acme.example.com), a path segment (/orgs/acme/…), an organization chosen after sign-in, or claims in an API token. Whichever is used, the tenant is resolved once, early in the request, verified against the authenticated user's memberships, and passed explicitly to everything downstream.

A tenant ID taken from the request without checking membership is an access-control bug waiting to happen.

  1. User

    authenticated

  2. Tenant context

    resolved, membership verified

  3. Authorization

    role in this tenant

  4. Tenant data

    scoped queries, files, jobs

Tenant request flow: an authenticated user's tenant context is resolved and verified against their membership, authorization checks their role in that tenant, and only then is tenant-scoped data accessed.

Data isolation

There are three common models. A shared database with a tenant_id on every row is the cheapest to run and simplest to evolve, and it pushes isolation into the code. Schema-per-tenant gives stronger separation inside one database server, at the cost of running every migration once per tenant. Database-per-tenant gives the strongest isolation and easy per-customer backup or data residency, with the highest operational cost.

For most B2B SaaS I start with shared tables and automatic scoping, and keep the option to move a large or regulated customer to dedicated infrastructure later.

Tenant scoping applied by default (Laravel global scope)
// Applied to every tenant-owned model, so queries are scoped by default.
class BelongsToTenantScope implements Scope
{
    public function apply(Builder $query, Model $model): void
    {
        $query->where(
            $model->qualifyColumn('tenant_id'),
            app(TenantContext::class)->id(),
        );
    }
}

Authorization

Authorization in a multi-tenant system has two layers that must both pass: is this user a member of this tenant, and does their role in this tenant allow this action on this resource? Resource IDs are never trusted on their own — loading /invoices/42 must also confirm that invoice 42 belongs to the current tenant, or sequential IDs make cross-tenant access trivial to probe.

User membership

Users and tenants are many-to-many: a consultant may belong to five client organizations with a different role in each. Membership carries the role, status and invitation history; the active tenant is part of the session; and switching tenants is an explicit action that re-checks membership. Invitations are scoped to one tenant, expire, and never grant access to any other.

Plans & billing

Subscriptions belong to the tenant, not the user. Features and quotas — seats, storage, projects — are evaluated per tenant, and each tenant needs a clear billing owner who can change the plan.

Downgrades raise the hard questions: what happens to the sixth project when the new plan allows five? Deciding that up front avoids support escalations later.

Related: SaaS Development expertise

Background jobs

Jobs run outside the request, so the tenant context that was implicit in the web request has to be passed explicitly: the tenant ID in the job payload, restored before any query runs. Scheduled jobs that work across all tenants process each one in its own context, and failures are logged with the tenant's identity so they can be investigated and retried individually.

A job without tenant context is a job that can process the wrong tenant's data.

File storage

Files follow the same boundary as data. Storage paths are prefixed with the tenant, access is checked against the database record — never inferred from the path — and downloads go through short-lived signed URLs issued after authorization. Exports and generated reports are tenant-owned objects with an expiry, not files left in a shared folder.

Related: AWS expertise

Reporting

Tenant-facing analytics query only that tenant's data, through the same scoping as the rest of the application. Cross-tenant reporting for the platform team is a separate, explicitly privileged path — ideally reading from a reporting store — rather than an “all tenants” switch added to customer-facing queries, which is how accidental leakage happens.

Admin tools

Support needs to see a tenant's status, plan, usage and members, apply overrides, and sometimes act on a customer's behalf. “Log in as” features are powerful and dangerous: they need explicit permission, a visible banner while active, and an audit trail of which staff member did what in which tenant.

Caching

Every cache key for tenant data includes the tenant ID. A key like dashboard_stats without one will eventually serve one customer's numbers to another — a leak that never shows up when testing with a single tenant. Invalidation is tenant-scoped too, so one tenant's update doesn't flush everyone else's cache.

Testing

Isolation gets dedicated tests: create two tenants with similar data, act as a member of one, and assert that every endpoint, export, search and job refuses or ignores the other tenant's resources. Tenant switching, removed memberships and role changes get tests too, and background jobs are tested with tenant context set explicitly — the places where scoping is most often lost.

Scaling

Tenants are never the same size. One large customer's import, report or integration can slow down everyone sharing the same database or queue — the noisy-neighbour problem. Per-tenant rate limits and fair queueing, heavy work moved to background processing, and the ability to move the largest tenants to dedicated resources address it.

Each step adds operational complexity, so it happens when measurements show the need, not on day one.

In practice

Common problems I work with

  • Queries that forget the tenant filter
  • Jobs and exports running without tenant context
  • Cache keys shared across tenants
  • Users who need access to several organizations
  • Cross-tenant reporting mixed into customer-facing code
  • One large tenant slowing everyone else down

Real products

How it connects to real products

Multi-tenancy was central to the collaboration platform I led, and the same boundaries shape the SaaS products I work on today.

  • Enterprise SaaS

    Multi-Tenant Collaboration Platform

    Led development of a multi-tenant, multi-user platform — tenant boundaries across data, files, APIs and the Vue.js frontend.

    Private client project

See all selected work

Related expertise

SaaS Development

Tenancy, permissions, plans, billing and the realities of running SaaS in production.

  • Multi-tenancy
  • Permissions
  • Billing
  • Production delivery

Laravel

Maintainable backends: structure, Eloquent, queues, authorization and SaaS patterns.

  • Application structure
  • Eloquent
  • Queues
  • Testing

API Development

Predictable API contracts: validation, errors, authorization, idempotency and webhooks.

  • API design
  • Error handling
  • Idempotency
  • Webhooks

AWS

Workload-first cloud architecture: serverless, storage, data, security and cost.

  • Serverless
  • S3 & DynamoDB
  • IAM
  • Cost awareness

Related services

  • SaaS Product Development

    Design and build SaaS products from MVP to production, including architecture, user management, subscriptions, integrations and cloud infrastructure.

    Learn more
  • Custom Software Development

    Build reliable web platforms, SaaS products, dashboards, APIs, and internal tools with modern full-stack technologies.

    Learn more
Let's talk

Have a technical challenge?

Whether it's an architecture decision, a delivery problem or something specific to Multi-Tenant SaaS, I can help you find a practical path forward.

What happens next

  1. Share your goal

    A few lines about the product, problem, or audit need.

  2. Get a quick read

    A practical reply on the best technical path forward.

  3. Move forward

    A clear scope, estimate, or concrete next step.