PostgreSQL for B2B SaaS
The database that handles multi-tenancy, analytics, and ACID compliance without breaking a sweat.
Variant Systems builds industry-specific software with the tools that fit the problem.
Why this combination
- Native schema-based multi-tenancy lets you isolate tenant data at the database level, not just the application level.
- Row-level security policies enforce data isolation even if your application code has a bug. Defense in depth matters.
- Advanced query capabilities like window functions, CTEs, and JSONB handle the analytics dashboards your customers expect.
- Proven reliability. PostgreSQL has been battle-tested for decades. Your SaaS data isn't an experiment.
Multi-Tenancy Done Right
Every SaaS product faces the same question: how do you keep one customer’s data separate from another’s? PostgreSQL gives you real options. Schema-per-tenant creates physical separation at the database level. Each tenant gets their own tables in their own schema. Queries can’t accidentally cross boundaries because the schema acts as a namespace.
For products where schema-per-tenant adds too much operational overhead, row-level security is the alternative. Define a policy once, and PostgreSQL filters every query by tenant ID automatically. Even if an engineer forgets a WHERE clause, the database catches it. This isn’t a nice-to-have. When your SOC 2 auditor asks how you prevent cross-tenant data access, “the database enforces it” is a strong answer. The practical implementation uses SET app.current_tenant at the start of each request, and your RLS policies reference that session variable. Combined with a connection-pooling layer that resets session state between requests, you get airtight isolation with zero per-query overhead in your application code.
Subscription Data Modeling
Subscription billing is deceptively complex. A customer signs up, upgrades mid-cycle, adds seats, gets a prorated refund, then downgrades. Every one of these events needs to be tracked with precise timestamps and amounts. PostgreSQL’s date range types and constraint exclusion handle temporal data cleanly.
Store plan changes as immutable events rather than overwriting the current plan. Use window functions to calculate current MRR, churn rates, and expansion revenue directly in SQL. Your finance team gets accurate numbers from the same database your application uses. No separate analytics warehouse needed until you’re well past product-market fit. PostgreSQL’s tstzrange type is particularly useful here — you can store the exact validity period of each subscription state and use range operators to answer questions like “what plan was this customer on at any given date” without scanning every event. Add an exclusion constraint to guarantee no overlapping subscription periods exist for the same customer, catching logic errors at the database level rather than in application code.
Analytics Queries Without a Warehouse
B2B SaaS customers expect dashboards. Usage metrics, team activity, billing history, and trends over time. PostgreSQL handles these analytical queries better than most teams expect. Common Table Expressions break complex queries into readable steps. Window functions calculate running totals, rankings, and moving averages without application code.
Materialized views let you precompute expensive aggregations. Refresh them on a schedule or on demand. Your dashboard loads in milliseconds because it reads from a precomputed table instead of running a complex join every time. When the dataset grows beyond what a single PostgreSQL instance handles comfortably, that’s when you add a read replica or a proper warehouse. But most SaaS products won’t need that for years.
Reliability Your SLA Depends On
Your SaaS uptime SLA is only as good as your database’s uptime. PostgreSQL’s crash recovery is rock solid. WAL (Write-Ahead Logging) ensures that committed transactions survive server crashes. Streaming replication gives you a hot standby that can take over in seconds if the primary fails.
Connection pooling with PgBouncer handles the connection storms that happen when your SaaS has hundreds of tenants making concurrent requests. Each PostgreSQL connection consumes roughly 5-10 MB of memory, so without pooling, a few hundred concurrent users can exhaust your database server. PgBouncer in transaction mode lets thousands of application connections share a small pool of actual database connections. Point-in-time recovery means you can restore to any second in the past, not just the last backup. When a customer accidentally deletes their data on a Friday afternoon, you recover it before they finish writing the support ticket.
Compliance considerations
Common patterns we build
- Schema-per-tenant where each customer gets an isolated PostgreSQL schema with identical table structures.
- Row-level security policies that filter every query by tenant_id, enforced by the database regardless of application logic.
- Subscription billing tables with proper temporal data modeling for plan changes, upgrades, and prorated charges.
- Materialized views for dashboard analytics that refresh on a schedule, keeping read performance fast without denormalization.
Other technologies
Services
Building in B2B SaaS?
We understand the unique challenges. Let's talk about your project.
Get in touch