Why we gave every client their own database
One Supabase project per client costs more to run than one shared database for everyone. The isolation and the export story are why it was still the right call.

The short answer
- SalesCrew runs single-tenant: one Supabase project, with Postgres, storage and edge functions, per client instance. Not one shared database with a customer-ID column.
- The trade is real. A dedicated project per client costs more in total infrastructure than a shared database serving every customer would.
- What it buys: a structural isolation boundary instead of a policy-based one, a full database dump as the export instead of curated CSVs, and a data residency choice made per client at provisioning.
- The same architecture makes an agency's snapshot, one client's configured stages, cadences and agent policy, portable to the next client instance without touching anyone else's data.
The cheaper default, and why most SaaS CRMs pick it
Almost every SaaS product, CRMs included, runs multi-tenant. One database and one set of infrastructure serve every customer at once. Rows are separated by a customer-ID column, and application logic decides which rows a request may see. This is the cheaper architecture to operate. The vendor scales one system instead of one system per customer. That is why it is the default almost everywhere.
SalesCrew does not run this way. Each client instance gets its own Supabase project: its own Postgres database, its own storage, its own edge functions. That is a much more expensive choice to operate at scale. It was made anyway, because the two things a shared database structurally cannot offer, isolation and full export, matter more to a CRM than to most SaaS categories.
What a database boundary gives you that a permission rule cannot
In a shared database, isolation between customers is enforced by rules: row-level security policies, application-layer filters, careful query construction. Those rules can be correct for years and wrong once. When they are wrong once, the failure exposes one customer's rows to another. There was never a hard boundary between them, only a rule that was supposed to enforce one.
A separate database per client removes that failure mode by construction. No query can accidentally return another client's rows, because another client's rows are not in the database being queried. SalesCrew still applies row-level security and a three-axis scope model, channel, Upwork profile and area, inside each client's project. A locked building still has locked rooms inside it. The project boundary and the RLS policy do different jobs, not the same job twice.
The other place this shows up is export. A shared-database vendor can hand a customer CSVs of the objects it chose to expose. It cannot hand over the database, because the database also holds every other customer's data. A single-tenant database has no such constraint. A client's export is a full Postgres dump plus CSVs everywhere, because nothing else lives in that project. On cancellation, SalesCrew's pattern is read-only access, then export, then deprovisioning after 90 days as a human-triggered step, not an automatic delete. That is only safe to offer because the client's data was never mixed with anyone else's.
What this made possible beyond isolation
The same per-client project structure makes data residency a real choice instead of a marketing line. Provisioning a client means standing up a new Supabase project, so the region is picked at that moment, per client. It is not one region the whole platform runs in that every customer inherits, wherever their business operates.
It also underlies a feature that would not make sense in a shared-database product: snapshots. A snapshot captures one client's configuration, its pipeline stages, cadences, dispositions and agent policy, as an exportable object, and applies it to another client's instance. That works cleanly because each instance is its own project with its own configuration. Applying a snapshot means configuring a project, not filtering rows inside a database everyone shares. For an agency running several client instances, this turns "we configured one client well" into "we configured every client the same way in an afternoon". In practice that is the bigger benefit of the architecture, beyond isolation alone.
The honest cost is real. A per-client database is more expensive to run than a shared one. It is a smaller, newer approach than the multi-tenant default most CRMs use, tested at less scale than a decade-old multi-tenant platform. For a product built around agents writing to customer data, and around agencies reselling configured instances, the trade still points toward a boundary that does not depend on a rule staying correct forever.
Questions
- Isn't row-level security enough to isolate customers in a shared database?
- Row-level security is a real control, but it is one policy away from a mistake. A single misconfigured or overly permissive RLS rule in a shared database can expose rows across every customer at once. The boundary is a rule the application must get right, not a structural separation between databases.
- Does single-tenant mean SalesCrew costs more to run?
- Yes, generally. A dedicated Supabase project per client costs more in total infrastructure than one shared database serving every customer. That is the trade a single-tenant vendor makes: higher operating cost for stronger isolation and portability.
- Can data residency actually be chosen per client this way?
- Yes. Each client's data lives in its own Supabase project, so the region for that project is a provisioning choice. It is not a shared setting that applies to every customer on the platform at once.