HADOOPT
TECHNOLOGIES
Schedule a Tech Consultation
Home
Services
Odoo ERP implementation Rollout, custom modules, migration and support. Web application development SaaS products, portals and internal platforms. Mobile application development Offline-first field apps and customer apps. AI development & automation LLM assistants, document processing, forecasting. Odoo version upgrades ↗ Move to a newer release, scoped and priced on its own site.
Products
OrgExp ↗ Budget, CAPEX and OPEX management.
Blog About Contact Schedule a Tech Consultation
Blog / Web application development
Web apps 7 min read

Multi-tenant SaaS architecture basics: isolation, auth, billing and data residency

Database-per-tenant, schema-per-tenant or shared tables with row-level security; plus tenant-aware auth, billing, noisy neighbours and data residency.

· Platform practice 22 Sep 2026

Most new SaaS products should start with shared tables, a tenant_id on every row, and isolation enforced in the database with PostgreSQL row-level security. Move individual large or regulated customers to their own database when they need it, rather than building every tenant that way from day one. Whatever the model, put the tenant into authentication, billing, logging and backups from the first release, because retrofitting it is the expensive part.

The three tenancy models

A tenant is one customer organisation using your product. Multi-tenancy is how you keep their data apart while running one product. There are three common models, and they trade isolation against operational cost.

  • Database per tenant. Each customer gets its own database. Strongest isolation, easy per-tenant backup, restore and deletion, and a straightforward answer to "where is my data". The cost is operations: migrations run once per database, connection counts grow with tenants, and reporting across tenants needs extra work. Odoo itself works this way, with one PostgreSQL database per instance.
  • Schema per tenant. One database, a separate PostgreSQL schema per tenant. Isolation sits between the other two. Libraries such as django-tenants support it. It suits tens to hundreds of tenants, but migrations still run per schema and very large tenant counts become awkward.
  • Shared tables with a tenant column. Every tenant-owned table has a tenant_id, and every query filters on it. Cheapest to run, simplest to migrate and easiest for cross-tenant analytics. The risk is a missing filter that leaks data between customers, which is why the filter should be enforced by the database, not just the application code.
Database per tenantSchema per tenantShared tables + row-level security
IsolationStrongestMediumLogical, enforced by policies
MigrationsOnce per tenantOnce per schemaOnce
Onboarding a tenantCreate and migrate a databaseCreate and migrate a schemaInsert a row
Per-tenant restore or deletionSimpleFairly simpleNeeds scripted, tested tooling
Cross-tenant reportingHardModerateSimple
Data residency per customerPlace the database in any regionTied to the shared database's regionTied to the shared database's region
FitsFew large or regulated customersTens to hundreds of mid-sized tenantsMany small and mid-sized tenants

Enforcing isolation in the database

With shared tables, PostgreSQL row-level security (RLS) is the safety net. You enable RLS on each tenant-owned table and add a policy that only returns rows whose tenant_id matches a setting the application sets at the start of each request or transaction. A forgotten WHERE clause then returns nothing instead of another customer's invoices.

ALTER TABLE invoice ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON invoice
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

-- per request, inside the transaction:
SET LOCAL app.tenant_id = '…';
A minimal PostgreSQL row-level security policy for a shared-table design.

Hybrid designs are common and sensible: shared tables for most customers, and a dedicated database for the few who need it for size, performance or contract reasons. Design the code so the tenant's database connection is looked up per request, and the move is a data migration rather than a rewrite.

Authentication and authorisation per tenant

  • Resolve the tenant first. From the subdomain (acme.yourapp.com), a custom domain, or the signed-in user's organisation. Never trust a tenant ID sent from the browser without checking the user belongs to it.
  • Users can belong to several tenants. Accountants, consultants and group companies do. Model memberships with a role per tenant rather than a single tenant field on the user.
  • Enterprise customers will ask for single sign-on. Plan for SAML or OpenID Connect per tenant, and for mapping their groups to your roles. An identity provider or a well-maintained library saves building this yourself.
  • Put the tenant in every token, log line and background job. Jobs that run outside a web request are where tenant context most often goes missing.

Billing, limits and noisy neighbours

Billing is part of the architecture, not an add-on. Decide early what you charge for (seats, usage, plan features) and record it per tenant as events, so invoices can be explained line by line. Plan features should be switched by configuration per tenant, not by code branches. For Indian customers, invoices need GST treatment; if you already run Odoo, pushing subscription invoices into Odoo's accounting keeps finance in one place (see Odoo 19 subscriptions).

A noisy neighbour is one tenant whose load slows everyone else: a huge import, a report over years of data, an integration polling every second. The defences are ordinary but must be designed in:

  1. Rate-limit per tenant, not only per IP address, on APIs and logins.
  2. Move heavy work to background queues with per-tenant concurrency limits, so one tenant's exports cannot fill every worker.
  3. Set query timeouts and paginate everything, so no single request can scan a whole table.
  4. Measure per tenant. Tag metrics with the tenant so you can see who is consuming what, and price or limit accordingly.
  5. Promote outliers. A tenant that consistently needs more gets its own database or worker pool, which the hybrid design above makes possible.

Data residency and India's DPDP Act

Data residency is where customer data is physically stored and processed. Some customers, and some regulators, will ask. The practical choices are one region for everyone, a region per deployment (for example an India deployment and an EU deployment), or per-tenant databases placed in the customer's chosen region.

For Indian personal data, the Digital Personal Data Protection Act, 2023 applies. The DPDP Rules were notified on 13 November 2025 with a phased start, and the substantive obligations on data fiduciaries, including security safeguards and breach notification, are scheduled to apply from May 2027; check the current timeline, as it has been under review. On transfers abroad, the Act takes a negative-list approach: personal data may go outside India unless the central government restricts transfers to a particular country. The Rules also allow the government to require significant data fiduciaries to keep specified categories of personal data in India. Separately, CERT-In's existing directions require cyber incidents to be reported within six hours.

For a SaaS builder that means: know which tenants hold Indian personal data, keep an audit trail of processing, be able to export and delete one tenant's data on request, and have an incident process ready. None of this is legal advice; have counsel confirm what applies to your product.

Tenant context belongs everywhere a request goes: the database, the queue, the logs and the invoice.

For the stack around this, start at choosing a web application stack, and for the controls that sit alongside isolation, web application security essentials. If you are designing a SaaS product now, our web application team can review your tenancy model with you.

Questions we get asked

Should each SaaS customer have their own database?

Usually not at the start. Shared tables with a tenant column and PostgreSQL row-level security are cheaper to run and simpler to migrate for many small and mid-sized customers. A database per tenant is worth it for a few large, regulated or performance-sensitive customers, or where a customer needs data in a specific region. A hybrid design lets you move individual tenants to their own database later.

What is row-level security in PostgreSQL?

Row-level security is a PostgreSQL feature that filters which rows a database role can see or change, based on a policy attached to the table. In a multi-tenant app, the policy compares each row's tenant_id with a setting the application sets per request, so a query that forgets its tenant filter returns nothing rather than another customer's data. Table owners bypass it by default.

What is the noisy neighbour problem in SaaS?

A noisy neighbour is a tenant whose heavy usage, such as a large import, a long report or an integration polling constantly, slows the product for everyone else sharing the same servers or database. The usual defences are per-tenant rate limits, background queues with per-tenant concurrency caps, query timeouts, per-tenant metrics, and moving consistently heavy tenants onto dedicated resources.

Does India's DPDP Act require data to be stored in India?

Not as a general rule. The Digital Personal Data Protection Act, 2023 allows transfer of personal data outside India except to countries the central government restricts by notification. However, the DPDP Rules let the government require significant data fiduciaries to keep specified categories of data in India, and sector regulators can impose their own localisation rules. Confirm your position with legal counsel.

← Previous PWA, native app or responsive website: which one to build Next → Django vs FastAPI vs Node.js for business application backends
Keep reading
Web apps Choosing a web application stack: frontend, rendering, backend, database, hosting 7 min read Web apps Build or buy: custom software, SaaS, or configure what you have 6 min read Odoo 19 Installing Odoo 19 from source, Docker or packages 7 min read