Case Studies

Multi-Tenant SaaS
Architecture

Technical Guide
Who This Is For

A practical guide for CTOs and technical founders

If you are a CTO or technical founder evaluating how to architect a multi-tenant SaaS platform, this guide is written for you. We have designed and built multi-tenant systems across multiple B2B SaaS products — using different approaches for different contexts. This is not a theoretical overview. It is a practical breakdown of what each approach actually looks like in production, where it works, and where it breaks down.

Multi-tenancy means a single deployment of your software serves multiple customers — called tenants — while keeping their data and experience completely isolated from each other. It is the architectural foundation of almost every B2B SaaS product.

Getting this decision wrong early is expensive. Changing your tenancy model after you have live customers is one of the most painful refactors a SaaS team can face. The choice you make at the start shapes your database design, your deployment model, your security posture, your onboarding flow, and your ability to scale. There is no universally correct answer — the right approach depends on your product, your customers, your compliance requirements, and your growth trajectory.

The Three Approaches

Multi-tenancy patterns we have used in production

Schema-Per-Tenant

Each tenant gets their own database schema within a shared PostgreSQL instance. Tables are identical across schemas — the structure is the same, but data is completely separated at the database level. Strong isolation, moderate operational overhead.

Shared Schema with Tenant ID Isolation

All tenants share the same tables. Every table has a tenant_id column. The application enforces isolation by filtering on tenant ID in every query. Simplest to operate at scale — lowest overhead per tenant.

White-Label Subdomain Provisioning

Each tenant gets their own branded subdomain — acme.yourplatform.com — with their logo, color scheme, and configuration. Primarily a frontend and identity concern, typically combined with one of the data isolation approaches above.

Approach 1

Schema-Per-Tenant

Each tenant is provisioned their own schema within a shared PostgreSQL database. When a request comes in, the application identifies the tenant from the request context — typically from a JWT claim, a subdomain, or a header — and sets the PostgreSQL search_path to that tenant's schema before executing any query.

Database: saas_platform
├── Schema: tenant_acme
│   ├── users
│   ├── orders
│   └── settings
├── Schema: tenant_globex
│   ├── users
│   ├── orders
│   └── settings
└── Schema: public (shared config, tenant registry)

In a Spring Boot application, this is handled through a custom TenantContext that holds the current tenant identifier in a thread-local variable, combined with a Hibernate CurrentTenantIdentifierResolver and a MultiTenantConnectionProvider that switches the schema on each connection. A filter or interceptor resolves the tenant from the incoming request and sets it in the context before any service layer code runs.

public class TenantContext {
    private static final ThreadLocal<String> currentTenant = new ThreadLocal<>();

    public static void setTenant(String tenantId) {
        currentTenant.set(tenantId);
    }

    public static String getTenant() {
        return currentTenant.get();
    }
}

Works well for: Platforms with a moderate number of high-value enterprise tenants, strong data isolation requirements, or compliance obligations (healthcare, government, financial services). If your customers will ask "where is our data stored and is it separate from other customers," schema-per-tenant gives you a clean, defensible answer.

Breaks down when: You scale to hundreds or thousands of tenants — schema sprawl, connection pooling complexity, and migration orchestration become significant operational burdens.

Approach 2

Shared Schema with Tenant ID Isolation

All tenants share the same tables. Every table has a tenant_id column that identifies which tenant each row belongs to. The application enforces isolation by including WHERE tenant_id = ? in every query. In Spring Boot with Spring Data JPA, this is typically implemented using a @Filter annotation on entities, activated via TenantContext on each session — or more robustly, through a custom repository base class that enforces tenant scoping on all queries.

Table: users
| id | tenant_id | name       | email              |
|----|-----------|------------|--------------------|
|  1 | acme      | John Smith | john@acme.com      |
|  2 | globex    | Jane Doe   | jane@globex.com    |

Table: orders
| id | tenant_id | amount | status   |
|----|-----------|--------|----------|
|  1 | acme      |   5000 | complete |
|  2 | globex    |   8000 | pending  |

Works well for: High-volume B2B SaaS with many small-to-medium tenants where operational simplicity and onboarding speed matter. Consumer-facing SaaS, SME-focused tools, and platforms where tenants are unlikely to have enterprise data compliance requirements. Adding a new tenant is just inserting a record — no schema provisioning required.

Critical risk: Row-Level Security (RLS) in PostgreSQL is an increasingly popular complement — it enforces tenant isolation at the database level as a safety net even if application-level filtering has a bug. Application-level filtering alone is necessary but not sufficient. A single missing WHERE clause can expose all tenant data. All indexes on tenanted tables must include tenant_id as the leading column or query performance will degrade dramatically at scale.

Approach 3

White-Label Subdomain Provisioning

Each tenant gets their own branded subdomain — acme.yourplatform.com, globex.yourplatform.com — with a customized experience: their logo, color scheme, domain, and potentially their own configuration. The application resolves the tenant from the subdomain on each request. This is often combined with one of the two data isolation approaches above — subdomain provisioning is primarily a frontend and identity concern, not a data storage decision.

Incoming request: https://acme.yourplatform.com/dashboard
        │
        ▼
Middleware extracts subdomain: "acme"
        │
        ▼
Tenant config loaded: { logo, theme, features, tenantId }
        │
        ▼
Request proceeds with tenant context set

The key infrastructure components are: a wildcard DNS record (*.yourplatform.com) routing all subdomains to your load balancer, a wildcard TLS certificate covering all subdomains, a tenant resolver middleware extracting the subdomain from the Host header, and a database table or cache mapping subdomains to tenant branding, settings, and feature flags.

Works well for: B2B SaaS where white-labeling is a core product requirement — agency tools, reseller platforms, clinic or franchise management systems. If your sales pitch includes "your customers won't know you're using our platform," this approach is essential. Plan carefully for custom domains — if tenants want their own domain, certificate automation becomes a significant engineering concern.

Comparative Summary

How the three approaches compare

Factor Schema-Per-Tenant Shared Schema Subdomain Provisioning
Data isolation Strong Moderate Depends on data layer
Operational complexity Medium Low Medium–High
Onboarding speed Slower Fast Medium
Scale to many tenants Moderate High High
Enterprise compliance Strong Weaker Neutral
Custom branding Not applicable Not applicable Strong
Migration complexity Per-schema Single migration Single migration
Cross-tenant analytics Complex Simple Depends on data layer
Implementation effort High Medium Medium–High
How We Choose in Practice

Four questions that drive the decision.

When we architect a new multi-tenant platform, we do not start from a preference — we start from the product's reality. In several of our production systems we have combined approaches: shared schema for data storage, combined with subdomain provisioning for the frontend experience. This gives operational simplicity on the backend with a strong white-label experience on the frontend.

Discuss Your Architecture

How many tenants in year one and year three?

Ten enterprise clients points toward schema-per-tenant. Ten thousand SME clients points toward shared schema. The expected volume is the most important input to this decision.

What are your customers' data compliance expectations?

Healthcare, government, and financial services clients will often ask about data segregation explicitly. Schema-per-tenant gives you a clean, defensible answer when procurement teams ask "where is our data stored."

Is white-labeling a product requirement or a nice-to-have?

If your go-to-market depends on tenants presenting the product as their own, subdomain provisioning is not optional — it is a core feature that must be scoped from day one.

What is your team's operational capacity?

Schema-per-tenant and subdomain provisioning both add operational overhead that a small team needs to be ready for. Shared schema is the most operationally forgiving choice for early-stage products.

Common Mistakes to Avoid

What goes wrong in production

Choosing schema-per-tenant for a high-volume, low-value tenant model

If you plan to have thousands of tenants paying small amounts, the operational overhead of schema-per-tenant will cost you more than you make per tenant. Shared schema is the right call.

Implementing shared schema without Row-Level Security

Application-level tenant filtering is necessary but not sufficient. A single missing WHERE clause can expose all tenant data. RLS at the database level provides a critical safety net that is not optional.

Underestimating custom domain complexity

Wildcard subdomains are straightforward. Custom domains — where tenants bring their own domain — require certificate automation, CNAME verification, and DNS propagation handling. Plan for this explicitly if it is on your roadmap.

Not indexing tenant_id as the leading column

In shared schema implementations, every query filters by tenant_id. If your indexes do not lead with tenant_id, query performance will degrade dramatically as your data volume grows — often only discovered in production.

Delaying the multi-tenancy decision

This is the most expensive mistake. Retrofitting multi-tenancy into a single-tenant application is one of the most disruptive refactors a SaaS team can undertake. Make the decision before you write your first entity class.

bg-cta
cta-main-wrapper

Planning a SaaS
platform?
Let's talk.

We have designed and built multi-tenant SaaS platforms across healthcare, civic tech, fintech, and enterprise software — in production, at scale. Get the architecture right from the start.