Insights / Data, Privacy & Security · · 11 min read

Tenant isolation explained: separate databases, schemas and row-level security

Tenant isolation decides whether one customer's data can ever appear in another customer's screen, report or AI answer. We compare separate databases, separate schemas and row-level isolation, and explain why Oryvelon isolates at two levels: between companies and between each company's customers.

Tenant isolation is the answer to one plain question: can one customer's data ever show up for another customer? Not in theory, not "only if someone makes a mistake" — can it happen? In a multi-tenant product, where many customers share the same application, the honest answer depends on how the data is separated, where the separation is enforced and how much of the system sits outside the main database.

This article explains the three common tenant isolation patterns — separate databases, separate schemas and row-level isolation — and what each one costs. It also explains how we apply isolation at Oryvelon, where the question has two layers. Our companies must be isolated from each other, and inside each company, every customer must be isolated from every other customer.

What a "tenant" actually is

A tenant is the unit of ownership in a shared system. In a B2B product it is usually an organisation: a store, a school, an agency. In a consumer product it may be an individual account or a household. The tenant is whoever the data belongs to, from the customer's point of view.

Getting this definition right matters more than the technology. For MerchNivo, the tenant is a Shopify store. A merchant who runs two stores has two tenants, because the stores have different products, customers and numbers, and a daily briefing that blended them would be wrong. For EduRelia, the tenant in a licensing deal is the school, and students sit inside it with even tighter rules. For CastLyra, tenants are brand accounts and talent profiles, each with their own visibility and contact-detail rules.

Once the tenant is defined, isolation means that every piece of tenant data carries its owner, and the system refuses to hand it to anyone else.

Three patterns for tenant isolation

Most multi-tenant systems use one of three patterns, or a mix of them.

Separate databases per tenant

Each tenant gets its own database. The application connects to the right database based on who is signed in.

This is the strongest boundary. A query cannot accidentally read another tenant's rows because those rows are not there. Backups, restores, exports and deletion are all per tenant, which makes "delete everything for this customer" a clean operation. Some customers — schools, larger enterprises — will ask for this outright.

The cost is operational. Migrations have to run across every database. Connection pooling gets harder. Reporting across tenants, even for your own internal metrics, needs a separate pipeline. With thousands of small tenants, the overhead can outweigh the benefit.

Separate schemas per tenant

All tenants live in one database server, but each has its own schema: its own set of tables. The application sets the schema for each request.

This is the middle ground. The boundary is still structural, and a missing filter will not usually leak data, because the other tenant's tables are in a different namespace. Operations are simpler than one-database-per-tenant, but migrations still multiply and the database's catalogue grows with the number of tenants.

Shared tables with row-level isolation

All tenants share the same tables. Every row carries a tenant identifier, and every read and write is filtered by it.

This is the cheapest and most common pattern for software with many small tenants. It scales well, keeps migrations simple and makes internal reporting easy. It also puts the entire boundary on one thing: the tenant filter being applied every single time. One forgotten WHERE tenant_id = ? in a new report and the boundary is gone.

The fix is to stop relying on developers remembering. Databases that support row-level security can enforce the filter themselves, based on a tenant value set for the session. Alternatively, all data access goes through one layer that injects the tenant filter and refuses queries without it.

Comparing the patterns

Separate database Separate schema Row-level (shared tables)
Strength of boundary Strongest Strong Depends on enforcement
Cost per tenant Highest Medium Lowest
Migrations Per database Per schema Once
Per-tenant deletion and export Simple Fairly simple Needs careful queries
Cross-tenant internal reporting Hard Moderate Easy (and easy to misuse)
Typical fit Few, large or sensitive tenants Tens to hundreds of tenants Many small tenants

There is no universally correct choice. The right pattern depends on how many tenants you expect, how sensitive the data is and what your customers are entitled to ask for.

Isolation at two levels: why Oryvelon isolates per company and per customer

Oryvelon is a company builder. We build, own and operate independent companies, and we share infrastructure between them where it saves time and money. What we do not share is data. We explain the principle in Shared infrastructure, separate data; tenant isolation is how it becomes real.

That gives us two levels of isolation.

Level one: between companies. Each company has its own data stores, its own credentials and its own access list. MerchNivo's database does not contain EduRelia's tables in a different schema; it is a different store altogether. There is no shared user table across the group and no cross-product identity. This is effectively the "separate database" pattern, applied at the company level, and it is not negotiable. It is also what makes it possible for a company to stand alone — to be sold, spun out or shut down without untangling its data from everyone else's.

Level two: between each company's customers. Inside a company, the pattern depends on the product. A product with many small tenants, like a Shopify operations tool, may use shared tables with enforced row-level isolation. A product that serves schools may justify stronger separation for each licensed institution. The source-of-truth document for each company records which pattern it uses and why (see Why every company starts with a source-of-truth document).

The two levels answer different threats. Company isolation protects against the group itself becoming a data risk — the temptation to combine audiences, the blast radius of a single breach, the complexity of a merged system. Customer isolation protects against the everyday bug that shows one merchant another merchant's revenue.

Where tenant data leaks in practice

Most tenant leaks do not come from the main database. They come from everything around it. When we review a product for isolation, these are the places we look first.

  • Caches. A cached response keyed by a URL or a question, but not by tenant, will happily serve store A's answer to store B. Every cache key must include the tenant.
  • Search indexes. A shared full-text or vector index that is filtered after retrieval can still leak through ranking, counts or snippets. Filter at query time, or keep separate indexes.
  • Background jobs. A job queued with a record ID but no tenant context can run with the wrong permissions or write results into the wrong place.
  • Logs and error reports. Stack traces and request logs often contain payloads. If the whole engineering team can read all logs, the logs are a cross-tenant view.
  • Exports and file storage. A predictable file path or a public bucket defeats every database control.
  • AI context. If an AI feature builds its prompt from retrieved documents, the retrieval step is a data-access path and needs the same tenant filter as everything else. We cover this in RAG without data leakage.
  • Analytics. Product analytics that capture free text or identifiers can recreate tenant data in a tool nobody thinks of as a database.

A useful rule: derived data belongs to the tenant it came from. Summaries, embeddings, cached answers, aggregates and model outputs all inherit the tenant of their source.

AI makes isolation harder, not different

AI features add new paths for data to travel, but the principle stays the same. Our shared AI gateway handles routing, budgets, safety hooks and observability for every company. It deliberately does not merge prompts, knowledge bases, keys or users across products.

In practice that means:

  1. Every AI request carries the company and the tenant it belongs to.
  2. Retrieval only searches that tenant's knowledge.
  3. Response caches are keyed by company, tenant and prompt version, never just by the question.
  4. Observability logs store what is needed to debug and measure — tokens, latency, errors, schema validation results — with tenant content minimised and access restricted.
  5. No tenant's data is used to improve another tenant's answers.

Take a concrete MerchNivo case. Imagine a store sees a spike in refunds on a single product. The daily briefing flags it and suggests checking the product page and recent supplier batches. The numbers come from that store's data. The explanation may be written by a model. But the model is only ever shown that store's figures, and the cached version of the briefing can only ever be served back to that store. Another merchant with a similar product never sees the signal, and the signal is never "learned" into a shared pool.

Enforcing the tenant boundary in code

Whatever pattern a product uses, a few engineering habits make the boundary hold.

Resolve the tenant once, early. The tenant should be determined at authentication and attached to the request context. Handlers should not accept a tenant ID from a form field or a URL parameter and trust it.

Enforce in one place. Either the database enforces row-level rules, or a single data-access layer applies them. Hand-written filters scattered across hundreds of queries will eventually miss one.

Fail closed. A query without tenant context should error, not return everything. This is a small design choice that prevents the worst outcomes.

Make cross-tenant access explicit. Some internal jobs genuinely need to see across tenants — billing, support tooling, aggregate health checks. Give them a separate, named path with its own permissions and logging, instead of letting them bypass the normal layer.

Test for leaks on purpose. Create two synthetic tenants in test, sign in as one, and try to read, update, export, search and prompt your way into the other. Automated tests for this are cheap and catch regressions that code review misses. This depends on having good environments and synthetic test data.

Keep secrets per company. Database credentials, storage keys and model provider keys are separate for every company and every environment, so that a leaked key only opens one door. See Secrets management basics.

Choosing a pattern: a worked comparison

Consider three of our companies side by side. These are illustrations of how we reason, not claims about scale.

MerchNivo. Many stores, each with modest data volume, all using the same features. Shared tables with database-enforced row-level isolation fit well. The data is commercially sensitive — revenue, margins, customer orders — so enforcement must be structural and cross-tenant tests run on every release. Store-level export and deletion are designed in from the start, because a merchant who uninstalls should be able to leave cleanly.

EduRelia. Schools licensing a product for students is a different risk profile. The data concerns children, the school is typically the data controller, and contracts can require specific handling. Stronger separation per school is easier to explain, easier to audit and simpler to delete at the end of a contract. The extra operational cost is worth paying. We discuss the wider approach in Data minimisation for children in edtech.

KeşifAtlası. Users answer questions about nationality, residence and circumstances to check visa and relocation eligibility. Most users are individuals rather than organisations, so each account is its own tenant. The strongest protection here is minimisation: collect only what the verified rules database needs to evaluate eligibility, and keep report data separate from marketing data.

The pattern follows the data, the customer and the contract — not a group-wide default.

Common mistakes

We see the same errors repeatedly in multi-tenant systems, including early prototypes of our own.

  • Treating the admin panel as outside the boundary. Internal tools are often built fast and given broad access. They should respect tenants too, with cross-tenant views limited to the people who need them.
  • Tenant ID in the URL, trusted without checking. Changing /store/123/ to /store/124/ should never work.
  • Shared caches added late. Caching is usually introduced for performance under pressure, which is exactly when the tenant key gets forgotten.
  • Assuming the vector database is "just an index". It holds tenant content in another form and needs the same controls.
  • One set of production credentials for everything. A single key that can read every company's storage turns a small mistake into a group-wide incident.
  • Deletion that only touches the main table. Backups, search indexes, caches and analytics need a deletion story too. See Data retention and deletion.

A tenant isolation checklist

Before a product goes live, and again when significant features ship, we walk through a short list.

  1. The tenant is defined in writing, including edge cases (one person, two stores; one school, many campuses).
  2. The chosen isolation pattern is recorded with the reasoning.
  3. Tenant context is resolved at authentication and enforced in one layer or by the database.
  4. Queries without tenant context fail.
  5. Caches, search indexes, file paths and AI retrieval all include the tenant.
  6. Logs minimise tenant content and are access-controlled.
  7. Cross-tenant internal paths are named, restricted and logged.
  8. Automated tests attempt cross-tenant reads, writes, exports and AI queries.
  9. Per-tenant export and deletion work end to end.
  10. Credentials are per company and per environment.

None of this is exotic. It is the discipline of doing the ordinary things every time, in every product.

What isolation costs, and why we pay it

Isolation is not free. Separate data stores per company mean more infrastructure to run and more migrations to manage. Strict tenant enforcement slows down the quick internal report someone wants on a Friday afternoon. Some growth ideas — "email everyone who uses any of our products" — are simply unavailable to us. We are comfortable with that; the reasoning is in Separating marketing consent from product consent.

What we get in return is a portfolio where a problem in one product stays in one product, where customers can be told plainly who holds their data and why, and where each company remains a clean, independent business. For a group whose whole model is building companies that can stand on their own, that trade is easy.

Questions to ask any multi-tenant vendor

The same thinking works in reverse when we evaluate a tool that will hold our data. A vendor's security page rarely answers the useful questions, so we ask them directly.

  • How is a tenant defined in your system, and can one of our companies be a separate tenant from another?
  • Is the tenant boundary enforced by the database, by a single access layer, or by individual queries?
  • Do caches, search indexes and any AI features respect the same boundary?
  • Can your support staff see our data, and if so, is that access logged and time-limited?
  • Can we export and delete everything for one tenant, including derived data?
  • Is our data used to train or improve features for other customers?

Vague answers are an answer in themselves. A vendor that has thought about isolation can usually explain its approach in a few sentences.

Summary

Tenant isolation is what guarantees that one customer's data never reaches another. The three main patterns — separate databases, separate schemas and row-level isolation on shared tables — trade boundary strength against operating cost, and the right choice depends on the number of tenants, the sensitivity of the data and what customers are entitled to ask for. At Oryvelon we isolate twice: every company has its own data stores with no shared user identity, and inside each company every customer is a tenant with a structurally enforced boundary. Whatever the pattern, the boundary only holds if the tenant is resolved once, enforced in one place, extended to caches, indexes, logs and AI context, and tested deliberately for leaks.

Questions and answers

What is tenant isolation?

Tenant isolation is the practice of keeping each customer's data separated so that no other customer can read, infer or receive it. It covers databases, files, caches, logs, search indexes and anything an AI model is given as context.

Is row-level security enough for a multi-tenant SaaS product?

It can be, if the tenant filter is enforced by the database or a single data-access layer rather than by each query, and if cross-tenant tests run on every release. Higher-risk data often justifies a stronger boundary such as a separate schema or database.

Does Oryvelon share one database across its companies?

No. Each Oryvelon company has its own data stores, and inside each company, customers are isolated as separate tenants. Shared infrastructure is limited to tooling and patterns, never user data.

NextChild data minimisation in edtech: what to collect, what to refuse and who decides →