Database-Per-Tenant: Why We Chose True Multi-Tenancy for Vectis
When building a SaaS application, you have to decide how to isolate customer data. The two main approaches:
- Shared database with row-level filtering — All customers in one database, every table has a
tenant_idcolumn - Database-per-tenant — Each customer gets their own database
Most SaaS products choose option 1. It’s simpler to build, easier to manage, and scales well for most use cases. We chose option 2 for Vectis. Here’s why.
The Row-Level Filtering Approach
In a shared database model, every query includes a tenant filter:
SELECT * FROM orders WHERE tenant_id = 'acme-corp' AND status = 'pending';
This works, but it has risks:
One bug exposes everyone’s data. Forget the WHERE tenant_id = ? clause in one query and you’re leaking data across tenants. Every developer, every code review, every query has to get this right.
Performance isolation is hard. One tenant running a heavy report can slow down everyone else. You can add query timeouts and resource limits, but you’re still sharing the same database engine.
Compliance gets complicated. Some customers want their data in specific regions. Some want the ability to export and delete everything. With a shared database, these requests require careful surgery.
Backups are all-or-nothing. You can’t easily restore one tenant’s data without affecting others.
The Database-Per-Tenant Approach
With database-per-tenant, each customer gets complete isolation:
-- Connected to acme-corp's database
SELECT * FROM orders WHERE status = 'pending';
-- No tenant_id needed - you're already in their database
Security by architecture. There’s no tenant_id to forget. The connection itself determines which data you see. A bug in your query logic can’t leak data across tenants because the data isn’t there.
Performance isolation. Heavy queries affect only that tenant’s database. You could even give different customers different database sizes, though today every Vectis tenant gets the same autoscaling compute range.
Compliance is straightforward. Want to delete a customer’s data? Drop the database. Region moves become a per-tenant migration rather than a full-database surgery, though today Vectis provisions every tenant in the same Neon project.
Independent backups. Restore one tenant without touching others.
The Tradeoff: Operational Complexity
Database-per-tenant isn’t free. You’re managing potentially thousands of databases instead of one. This means:
- More connection management
- More complex migrations (run against every database)
- More monitoring and alerting
- Higher baseline costs (each database has overhead)
How We Made It Work: Neon Serverless PostgreSQL
The traditional database-per-tenant model was expensive because each database meant a running server. Even a small PostgreSQL instance costs ~$15/month. With 1,000 tenants, that’s $15,000/month just in database costs.
Neon changes this equation. It’s serverless PostgreSQL with:
- Scale to zero — Databases that aren’t being used don’t cost anything
- Instant provisioning — New database in milliseconds, not minutes
- Branching — Create database copies for testing without full duplication
- Per-tenant connection pooling — Efficient connection management built in
With Neon, we can provision a database for every tenant without the operational overhead or cost explosion.
The Implementation
When a new tenant signs up:
- Create a Neon branch for the tenant from an empty template, with its own autoscaling compute
- Run migrations and bootstrap the schema
- Record the branch in the control plane’s tenant registry
- Route all requests for that tenant to their database
The application layer handles connection routing:
async function getTenantConnection(tenantId: string): Promise<Database> {
const tenant = await tenantRegistry.get(tenantId);
return connectionPool.get(tenant.databaseUrl);
}
Every request carries the tenant’s slug (from its subdomain). We check it against the signed token, then resolve that tenant’s connection through the Neon API.
When Database-Per-Tenant Makes Sense
This architecture is worth the complexity if:
- Data sensitivity is high — Financial data, health data, or anything where a leak is catastrophic
- Compliance requirements vary — Different customers need different regions or retention policies
- Performance isolation matters — Enterprise customers don’t want to share resources with everyone else
For a simple B2C SaaS with low-sensitivity data, shared database is probably fine. For an OMS handling business-critical order and inventory data? We think true isolation is worth it.
Vectis uses database-per-tenant architecture via Neon serverless PostgreSQL. See how it works.
Disclaimer
This content is for informational purposes only. Individual results may vary based on your specific circumstances.