Engineering10 min read

Multi-tenancy: the decision you make once and live with forever

Almost every other architectural decision in a SaaS product can be revisited. The tenancy model is the one that hardens into everything else, so it is worth an afternoon of genuine thought.

Muhammad Qasim, author

Muhammad Qasim

Full Stack Developer · Rawalpindi, Pakistan

About the author

Short answer

Start with a shared schema and a tenant column enforced at the query layer, unless a specific compliance requirement or a genuinely enormous single tenant forces you to isolate further.

01Three models, honestly compared

There are effectively three options, and the trade-off is the same axis every time: isolation versus operational cost.

Shared schema puts every tenant in the same tables with a tenant identifier column. One migration, one connection pool, cheap to run, trivial to query across tenants for analytics. The risk is that a single missing filter in a single query leaks one customer's data to another.

Schema-per-tenant gives each tenant its own set of tables in one database. Isolation is much stronger and per-tenant backup and restore becomes straightforward. The cost is that every migration now runs N times, and connection and metadata overhead grows with tenant count.

Database-per-tenant is the strongest isolation and the highest operational burden. It is the right answer when a customer contractually requires their data in a specific region or on separate infrastructure, and the wrong answer when you chose it because it felt safer.

The question is not "which is most secure" but "which failure am I more likely to survive" — a leaked query, or an operational load I cannot sustain.

02Why I default to shared schema

For most products, and certainly for the reporting platform and pharmacy software I have built, shared schema is correct. The decisive argument is that the leak risk it introduces is solvable with engineering discipline, whereas the operational cost of per-tenant isolation is a permanent tax on every future change.

The discipline is making tenant scoping impossible to forget rather than something each developer must remember. In Django that means a default manager that filters by the current tenant, so an unscoped query is the thing you have to write deliberately rather than the thing you get by accident. Combine that with row-level security in PostgreSQL as a backstop and the failure mode moves from "likely" to "requires two independent mistakes".

  • Make the tenant-scoped manager the default and require an explicit, obviously-named escape hatch for cross-tenant queries.
  • Enable row-level security so the database enforces the boundary even if application code is wrong.
  • Put the tenant on the request context in middleware, never as a parameter callers pass in — parameters get forgotten.
  • Write a test that asserts a query from tenant A cannot read tenant B. Run it in CI.
  • Index on the tenant column first in composite indexes. Every query filters on it.

03The mistakes that force a migration

Almost every painful tenancy migration I have seen traces back to one of a few early decisions, none of which looked significant at the time.

Using a globally sequential identifier that customers can see. Once tenant A can infer from record 4,812 that you have roughly five thousand records in total, you have leaked business information, and switching to opaque identifiers later means rewriting every URL and integration.

Putting tenant configuration in application code or environment variables. It works for the first three customers and blocks self-service onboarding for the fourth. Tenant settings belong in a table from day one.

Treating the first customer as the system. If provisioning a new tenant requires you to run a script, you do not have a multi-tenant product; you have a single-tenant product you have installed twice.

If adding a customer requires an engineer, tenancy is not solved yet — however the data is partitioned.

04Getting the boring parts right early

The parts of multi-tenancy that hurt in year two are rarely the data model. They are onboarding, billing boundaries and the internal tooling you need to support customers.

Self-service provisioning should create the tenant, seed its defaults, invite the first user and be idempotent when someone double-clicks. Billing should map to the tenant, not the user, or the first customer who wants a second seat will break your assumptions. And you need an internal admin view that lets you look at a tenant's state without connecting to production directly — build it in the first month, because you will otherwise build it during an incident.

TopicsSaaSMulti-tenancyPostgreSQLDjangoArchitecture

Found this useful?

I write these from live project work. Follow along or get in touch if you want this kind of thinking applied to your product.

Quick answers

Which multi-tenant model should a new SaaS product use?+

Shared schema with a tenant column, enforced by a default query filter and row-level security. It is the cheapest to operate and the easiest to run analytics across. Move to schema- or database-per-tenant only when a specific compliance or data-residency requirement demands it.

How do you stop one tenant reading another tenant's data?+

Make tenant scoping the default rather than something developers apply. Resolve the tenant in middleware onto the request context, filter through a default manager, add PostgreSQL row-level security as an independent backstop, and keep a CI test that asserts cross-tenant reads fail.

Can you migrate from shared schema to per-tenant databases later?+

Yes, and it is genuinely painful — it touches connection handling, migrations, backups and anything that queried across tenants. That is the case for making the decision deliberately at the start rather than defaulting into it.

Should tenant identifiers be sequential integers?+

No. Customer-visible sequential identifiers leak volume information and make record enumeration possible. Use opaque identifiers such as UUIDs for anything that appears in a URL, an API response or an export.

Keep reading

All articles

Projects behind this article

The production systems where these decisions were made.

All case studies
  • Phamanovix Pharmacy Management Software — Pharmacy software project preview
    Pharmacy softwareMulti-tenant SaaS

    Phamanovix Pharmacy Management Software

    A multi-tenant pharmacy software product with inventory, orders, prescriptions, suppliers, staff management,

    Read case studyLive
  • Veeivs Reporting Analytics Platform — SaaS reporting project preview
    SaaS reportingAnalytics dashboard

    Veeivs Reporting Analytics Platform

    A SaaS reporting and ranking platform for multi-branch performance: live centre stats, agent and team lead r

    Read case studyLive
  • Keynou Tools All-in-One Utility Platform — Online tools platform project preview
    Online tools platformSaaS utilities

    Keynou Tools All-in-One Utility Platform

    An all-in-one online tools platform with image, PDF, video, converter, compressor and QR utilities, built wi

    Read case studyLive
  • Chopline Barber Booking SaaS — Booking SaaS project preview
    Booking SaaSBarber shop software

    Chopline Barber Booking SaaS

    A US multi-tenant barber shop booking SaaS with appointments, filters, Stripe payments, shop management and

    Read case studyLive

Related services

If this describes a problem you have right now, these are the ways I can help.

All services
  • SaaS Development

    Taking a SaaS idea to paying customers means solving multi-tenancy, billing, permissions and analytics before you wr

    Explore service
  • Django & Backend

    The backend is where a product either scales or quietly rots. I design data models that match the business, APIs tha

    Explore service
  • API & Integrations

    Integrations fail quietly. I build them with retries, logging and clear failure states, so when a third-party servic

    Explore service
  • CRM Development

    Off-the-shelf CRMs force your team to work the way the software wants. I build CRM systems around the workflow you a

    Explore service