This article compares the most documented Postgres tenancy models (shared schema, per tenant base, dedicated cluster, also called database-per-tenant vs shared database), explains the noisy neighbormechanism, then details how Aurabase implements its own two-tier model, verified in the provisioner code. For the methodology we apply before publishing a performance figure, see our backend benchmark methodology.
If you are looking for the question of data leakage between two clients (RLS, policies, service_role), this is not the angle of this article: our RLS comparison and dedicated database by project covers this logical isolation in detail. Here, we are talking about physical resources: CPU, IO, connections, cache.
The essentials
- A shared database is not necessarily a shared scheme: Aurabase gives each project its own Postgres database, even on its standard levels, by only sharing the cluster.
noisy neighbordegrades physical resources (CPU, IOPS, connections, autovacuum), not data confidentiality: RLS does not solve it, that is not its role.- The Aurabase provisioner routes to exactly two architectures, verified in the code:
FullyDedicated(entire CNPG cluster reserved for a project, enterprise level) orSharedClusterDedicated(dedicated base on the organization's CNPG cluster, never shared with another organization). - An Aurabase fleet cluster has a default observed capacity threshold of 1000 bases per cluster, configurable, beyond which the recommendation is to migrate to a dedicated cluster.
- The right choice depends on your real constraints (compliance, traffic predictability, budget), not on a reflex “dedicated is always better”.
Microsoft's official documentation on the architecture of multi-tenant SaaS applications distinguishes three tenancy models, generally called Silo (dedicated resources per tenant), Pool (fully shared resources) and Bridge (a mixture of the two, some isolated tenants, others shared). These three models apply directly to Postgres, at the schema, database or entire cluster level.
Concretely, for a Postgres backend, this gives three distinct architectures. The shared schema (a single base, a tenant_idcolumn, RLS policies which filter the rows) is the most common Pool model in multi-tenant guides: economical, but the border between two clients becomes an SQL expression evaluated table by table. The base per tenant on a shared cluster is an intermediate Bridge model: each tenant has its own Postgres base (a real CREATE DATABASEcommand), but several bases coexist on the same physical cluster, therefore sharing CPU, IO and connections. The cluster fully dedicated per tenant is the complete Silo model: completely isolated CPU, RAM and IO resources, generally reserved for tenants with high compliance or load issues.
| Model | Resource insulation | Storage insulation | Operational Effort |
|---|---|---|---|
| Shared schema (tenant_id + RLS) | None | None (communal table) | Minimal (1 base to operate) |
| Per tenant basis, shared cluster | Partial (cluster CPU/IO) | Total (own basis) | Moderate (N bases, 1 cluster) |
| Fully dedicated cluster per tenant | Total | Total | High (1 cluster per tenant) |
Silo / Pool / Bridge terminology: official Microsoft documentation, multi-tenant SaaS architecture patterns (Azure Architecture Center).
The intermediate model, base per tenant on a shared cluster, is often absent from the guides which present the choice as binary between “a single base for everyone” and “one server per client”. However, this is the one Aurabase uses by default, detailed below.
The choice between these three models arises with every multi-tenant architecture decision, not just with a BaaS provider: a team that builds its own SaaS backend on managed Postgres (RDS, Cloud SQL, or a self-hosted instance) makes exactly the same arbitration, with the same contention mechanisms in play once several clients are placed on the same physical instance.
A noisy neighbor (noisy neighbor) is a tenant that consumes a disproportionate share of an infrastructure's shared resources, to the detriment of other tenants on the same server. The term comes from the public cloud, but applies directly to a shared Postgres cluster: one database can degrade the performance of others without ever touching their data.
Six mechanisms come up most often in production:
- CPU contention: an expensive query (join without index, massive sort) consumes CPU cycles that the kernel shares between all active databases in the cluster.
- IOPS contention: a backup, a
VACUUM FULLor a massive import saturates the cluster's disk throughput, slowing down reads and writes to other databases. - Connection exhaustion:
max_connectionscaps the number of active connections at the entire cluster level, not per base. A base that opens too much reduces the margin of others. - Autovacuum contention: autovacuum runs with a limited number of workers per cluster; a database with a high write rate can delay cleaning the tables of another database.
- Cache eviction:
shared_buffersis a single memory for the entire cluster; a database with a large working set can evict the cached pages of a smaller neighboring database. - Shared maintenance windows: backup, replica failover, or major upgrade apply to the entire cluster, not on a per-base basis.
Structural diagram, not a measurement result: no comparative performance figures published to date for these two topologies.
Connection budget is often the most visible symptom in production, even before latency. This is the detailed subject of our max_connections tuning guide and our comparison PgBouncer, Supavisor and PgCat.
A transaction mode pooler (PgBouncer, Supavisor, PgCat) mitigates connection exhaustion, but does not remove CPU or IOPS contention: it reuses existing server connections, it does not add additional CPU cores or disk throughput to the cluster. Our article on transaction pooling mode details what this mode actually changes, and what it doesn't change.
RLS isolates data, not resources
Row-Level Security solves a different problem: it prevents a query from reading or modifying the rows of another tenant, at the logical level. It reserves no CPU cycle, no connection slot, no disk throughput for a specific tenant.
Two tenants can have perfectly watertight RLS policies and degrade each other at the same time: the noisy neighbor is a problem of physical resources, not of access rights. Confusing the two leads to a false sense of operational security once EPIRB is in place.
For logical isolation (RLS policies, service_role, border between projects on the security side), see our dedicated article: RLS and dedicated base per project, the choice of multi-tenant isolation from Aurabase. This article remains on the level of physical resources.
The Aurabase model, verified in code
The provisioner code (aura-provisioner) documents exactly two possible architectures for an active Postgres project, from a provisioning consolidation that the code refers to as Task 12: FullyDedicated and SharedClusterDedicated. Legacy schema-grained models have been removed from the provisioning path.
The enterprise level triggers FullyDedicated: an entire CNPG cluster, reserved for this single project. All other levels (free, pro, team) route to SharedClusterDedicated: a full-fledged Postgres project_<uuid> database, on the CNPG cluster of the project organization. It is therefore not a shared schema: even on a standard plan, your database is a complete Postgres database, not one line among others in a common table. What is shared is the cluster (CPU, RAM, disk, connections), not the base itself.
An organization's CNPG cluster is created when its first Postgres project is provisioned and never hosts a project from another organization, a design choice locked at the code level (org_cluster.rs, Postgres advisory lock by organization at creation). The only possible noisy neighbor on the shared Aurabase landing is therefore another project of your own organization, never that of a third-party client.
Aurabase managed PostgreSQL cluster architecture specifications.
This threshold of 1000 bases per cluster is not a hard limit: it is an observability benchmark that triggers a recommendation to migrate to FullyDedicated, not an automatic block. It no longer drives any placement decisions, only one cluster is now possible per organization.
Each cluster, dedicated or fleet, exposes a CNPG Pooler (PgBouncer) which absorbs part of the pressure on active connections, in both topologies. What this pooler actually changes for resource contention is detailed below.
The CPU/RAM sizing of a shared cluster is also not uniform between organizations: it derives from the level of the organization via a dedicated function (FleetSizing::from_org_plan, verified in org_cluster.rs), and not from a single size applied to all levels. An organization at the team level does not size its cluster in the same way as an organization at the free level.
These two architectures run on PostgreSQL 16, not version 17, verified in the Dockerfile of the CNPG image used in production. This choice of version has its own tuning implications, detailed in our Postgres 16 vs 17 vs 18 comparison.
When pooling is enough, when dedicated becomes necessary
Pooling is not a cheap compromise. It corresponds to the traffic of the vast majority of projects in development, launch or moderate growth, where a dedicated cluster would be an additional cost without measurable benefit.
| Signal | Shared is enough | Dedicated recommended |
|---|---|---|
| Formal compliance on physical isolation (health, HR, public sector) | No | Yes |
| Predictable traffic, moderate peaks | Yes | |
| Unpredictable and sustained peak load | Risk of restraint | Yes |
| Tight budget, product in validation phase | Yes | |
| Contract clause (DPA) requiring documented insulation | No | Yes |
For projects subject to a contractual obligation for documented physical insulation, our DPA and compliance pages detail what each level covers.
The downside of the base-by-tenant model, highlighted by several schema migration management tools like Bytebase, is operational rather than technical: each migration must be applied and verified on each base, one by one, even when they coexist on the same cluster. A fully dedicated cluster does not eliminate this cost, it even adds it: one migration per cluster to monitor independently, rather than just one.
Resources specializing in multi-tenant software architecture, such as CodeOpinion, regularly present this intermediate approach (per tenant basis on shared infrastructure) as a reasonable compromise between the shared scheme and the fully dedicated cluster, rather than a binary choice between the two extremes.
Going from shared to dedicated does not require rewriting a schema or changing engines: in both cases, it is Postgres, with the same pg_dump / pg_restore chain as that described in our Supabase to Aurabase migration guide. Changing the level remains a toggle operation, not an application rewrite.
Frequently asked questions
What to remember
Dedicated base and shared base do not conflict on data security: both models can correctly isolate one tenant from another at the logical level. They oppose each other on physical resources (CPU, IOPS, connections, cache, maintenance windows). It is this plan that defines a noisy neighbor, not a poorly written RLS policy.
The Aurabase model, verified in the provisioner code, retains by default an intermediate compromise: a dedicated Postgres database per project, on a shared cluster but strictly reserved for a single organization, with a fully dedicated cluster reserved for the business level. The right choice depends on your real constraints, not on a reflex where the dedicated would always be the best option.