SaaS

SaaS tech stack: a practical architecture guide

The technology under a SaaS product matters less than founders fear and the architecture matters more. This guide goes layer by layer through the decisions a new SaaS product faces, with a default for each and the conditions under which the default is wrong.

SaaSUpdated September 21, 2026By the BBR engineering team

Short answer: build a modular monolith in a language your team knows, on PostgreSQL, with a shared schema and a tenant ID on every row. Buy authentication, billing, email delivery and monitoring. Run background work on a simple queue. Host it in containers or on a managed platform, in your own account. Depart from that only for a reason you can state in one sentence.

If you are choosing a stack for a first release of any kind, our MVP tech stack guide covers the general principles. This article deals with what is particular to SaaS: many customers sharing one system and paying for it monthly.

Principles before products

  • Familiar beats fashionable. A team ships fastest in the tools it knows. Mainstream tools also mean a larger hiring pool later.
  • Spend your architectural effort on what is hard to reverse: tenancy, identity and billing. Most other choices can be revisited.
  • Buy what is not your product. Nobody subscribes because of your password-reset flow.
  • Fewer moving parts. Every additional service is something to deploy, monitor, secure and pay for.
  • Own the accounts. Cloud, domain, billing, email and source control registered to your company from the first day.

Multi-tenancy: the defining decision

A tenant is a customer account: a company, workspace or organisation. Multi-tenancy is how one running system serves many of them while keeping their data apart. There are three established models.

Shared schemaSchema per tenantDatabase per tenant
How it worksOne database, one set of tables; every row carries a tenant IDOne database; each tenant has its own copy of the tables in a separate schemaEach tenant has its own database, sometimes its own server
IsolationLogical, enforced by application code and optionally by row-level securityStronger; a missing filter cannot cross schemasStrongest; physical separation
Operational effortLowest: one migration, one backup, one connection poolRising: every migration runs once per tenant; tooling needed past a few hundred tenantsHighest: provisioning, migrations, monitoring and backups per tenant
Cost per tenantNegligibleLowSignificant; small or free plans become uneconomic
Cross-tenant analyticsSimple queriesAwkwardNeeds a separate data pipeline
Per-tenant backup, restore, export, deletionNeeds tooling you writeEasierTrivial
Per-tenant data residencyOnly by running a separate regional deploymentSameNatural
“Noisy neighbour” riskHighest; needs rate limits and query disciplineModerateLowest
SuitsMost B2B and prosumer SaaS, many small to mid-sized tenantsTens to low hundreds of tenants wanting firmer separationFew, large, high-value tenants with contractual or regulatory isolation needs

The default: shared schema, done carefully

For most new products the shared schema is correct, and the risk it carries, a query that forgets its tenant filter, is manageable with discipline:

  • A tenant_id column on every tenant-owned table, not null, indexed, and part of composite unique constraints
  • Tenant scoping applied in one place (a data-access layer or query helper), so that individual features cannot forget it
  • PostgreSQL row-level security as a second line of defence where the framework allows it
  • Automated tests that create two tenants and assert that one can never read or change the other’s data, for every endpoint
  • The same scoping applied to file storage paths, cache keys, search indexes, background jobs and exports
  • Per-tenant rate limits so that one customer’s import cannot slow everyone down

Hybrid is normal

Mature products often run the shared model for most customers and a dedicated database or deployment for a few large ones who pay for it. If the application resolves “which database does this tenant live in?” through one lookup, that option stays open without building it on day one.

Model identity separately from tenancy. Keep users and organisations as separate entities joined by a membership that carries the role. People belong to several workspaces, change employers and act as outside consultants. A user table with a single tenant_id column is one of the most common early mistakes and one of the most tedious to undo.

Application framework and database

Any mature web framework can carry a SaaS product: Next.js with Node.js, Ruby on Rails, Django, Laravel, ASP.NET. They differ less than their advocates suggest. Choose on team experience and hiring market.

BBR’s own default is TypeScript throughout, with React and Next.js on the front end, Node.js behind it and PostgreSQL for data, because one language and shared types across the stack reduce both defects and the number of specialists a small company needs. That is a statement about what we know well, not a claim that other stacks are worse.

On the database there is less room for taste. SaaS business data (accounts, users, subscriptions, invoices, the objects in your workflow) is relational, and a relational database with transactions and constraints prevents whole classes of bugs. PostgreSQL also handles JSON columns, full-text search and vector search well enough to postpone adding separate systems for those.

Authentication and authorization

Authentication (who are you?) should be bought or taken from a well-maintained library. The realistic options:

OptionStrengthsWatch for
Managed identity service (for example Auth0, Clerk, Amazon Cognito, or the auth built into Supabase or Firebase)Fastest route to secure login, MFA, social login and enterprise SSO; security updates are someone else’s jobPer-user pricing as you grow; SSO often on higher tiers; migrating away takes planning
Framework library (the auth packages of your web framework)No per-user fees; users live in your database; full controlYou own session security, email flows and upgrades; SSO is additional work
Hand-writtenNone worth the riskPassword storage, token handling and reset flows are easy to get subtly wrong

If you expect to sell to larger companies, make sure the chosen route can support SAML or OIDC single sign-on later, even if it is not switched on at launch.

Authorization (what may you do?) is yours to build, because it encodes your product’s rules. Start with a few fixed roles per membership. Check permissions on the server for every request, in one central policy layer, never only by hiding buttons in the interface. Add custom roles when paying customers need them.

Subscription billing

Use a hosted billing provider. Stripe Billing is the most widely used; Paddle, Lemon Squeezy and Chargebee are established alternatives with different trade-offs. The first decision is structural:

Payment processor with subscription toolingMerchant of record
Who is the sellerYouThe provider, reselling your product
Sales tax and VATThe provider can calculate it; registration, filing and remittance remain your responsibilityHandled by the provider
FeesLowerHigher
FlexibilityGreatest: custom invoicing, usage billing, marketplacesMore constrained
SuitsCompanies with an accountant and B2B customers in a small number of jurisdictionsSmall teams selling worldwide to many small customers

Integration rules that prevent pain

  • The provider is the source of truth for subscription state. Your database keeps a synced copy of plan, status and period end for fast checks.
  • Sync by webhook, verify signatures, handle events idempotently, and expect them to arrive late or out of order.
  • Use the provider’s hosted checkout and customer portal at first. They handle card updates, invoices and strong customer authentication, and they keep card data off your servers.
  • Define plans and limits in one configuration, and have features ask “is this allowed for this tenant?” of one entitlement function. Pricing will change; this makes it a configuration change.
  • Handle failed payments deliberately: retries, reminder emails, a grace period, then a restricted state, never silent data deletion.
  • For usage-based pricing, record usage events in your own database first, aggregate, then report to the provider. You will need your own record when a customer questions an invoice.

Background jobs and scheduled work

Anything slow or unreliable belongs off the web request: sending email, processing webhooks, imports and exports, report generation, calls to third-party APIs and language models, nightly maintenance. You need a queue with retries, backoff, a dead-letter list for jobs that keep failing, and scheduled jobs.

A queue backed by the PostgreSQL database you already run is enough for many products and adds no infrastructure. A Redis-backed queue is the usual next step when volume grows. Make jobs idempotent, since they will sometimes run twice, and carry the tenant ID in every job so that scoping and per-tenant fairness survive the move off the request path.

Transactional email

Send through a transactional email provider (Postmark, Amazon SES, SendGrid, Resend and Mailgun are common choices), not from your own server. The engineering work is mostly in deliverability and hygiene:

  • Authenticate your sending domain with SPF, DKIM and DMARC
  • Keep transactional mail separate from marketing mail, ideally on different subdomains
  • Process bounces and complaints and stop sending to those addresses
  • Keep templates in code, with a plain-text version
  • Log what was sent to whom, so that support can answer “I never got the invitation”

Observability

Customers on a subscription notice downtime and tell you by cancelling. A small product needs four things from the start:

  • Error tracking (Sentry or similar) on both front end and backend, with the tenant and user attached to each event
  • Structured logs with request and tenant identifiers, searchable for at least a few weeks
  • Uptime checks and alerts that reach a person by phone or chat, not only a dashboard
  • Basic metrics: response times, queue depth, database load, error rate

Add two SaaS-specific items: an audit log of significant actions per tenant (larger customers will ask for it, and it settles support disputes), and product analytics that record the activation and usage events you will steer the business by. Full distributed tracing can wait until there is something distributed to trace.

Hosting and infrastructure

OptionGood forTrade-off
Managed application platform (Render, Railway, Fly.io, Heroku-style) with managed PostgreSQLSmall teams who want deployments, TLS and scaling handledHigher unit cost as you grow; some platform constraints
Docker containers on a cloud VM or VPS, with a managed or self-run databaseLow, predictable cost and full portabilityYou own patching, backups and failover, so they must be set up properly
Major cloud (AWS, Google Cloud, Azure) with managed container and database servicesEnterprise customers who expect it; compliance programmes; broad service catalogueMore configuration and more ways to overspend
Serverless and edge platformsSpiky traffic, front-end hostingConnection limits to relational databases, time limits on long jobs, harder local debugging

Whatever you choose: containerise the application so that you can move, keep staging as close to production as you can afford, automate deployments, take database backups with point-in-time recovery, and rehearse a restore. An untested backup is a hope, not a backup. Choose the hosting region with your customers’ data-protection expectations in mind, since moving it later is disruptive.

Monolith vs microservices

Start with a modular monolith: one deployable application, with clear internal boundaries between areas such as billing, identity, notifications and the core domain. It gives you one deployment, one place to debug, transactions that span features, and refactoring with the help of the compiler.

Microservices solve organisational problems, chiefly many teams needing to release independently, and they charge for it in network failure modes, distributed data consistency, duplicated tooling and operational load. A team of two to six engineers pays the full price and gets little of the benefit.

Reasonable grounds for extracting a service later:

  • A component with a very different resource profile, such as video processing or heavy document parsing
  • A component that needs a different runtime, such as a Python library with no equivalent in your main language
  • A separate team that must deploy on its own schedule
  • A security boundary, such as isolating a component that handles especially sensitive data

A background worker process that runs the same codebase as the web application is not a microservice. It is the monolith doing its slow work elsewhere, and you will want one early.

What to buy and what to build

CapabilityRecommendationReasoning
Card payments, subscriptions, invoicesBuyRegulated, intricate, and solved
AuthenticationBuy, or use a maintained libraryHigh security risk, no differentiation
Email deliveryBuyDeliverability is a specialism
Error tracking, logs, uptime monitoringBuyInexpensive; building them is a distraction
File storage and CDNBuy (object storage)Commodity
Product analytics, support inbox, status pageBuyCommodity
Feature flagsBuild simply at firstA table and a helper function cover early needs
SearchStart with PostgreSQL full-text searchMove to a dedicated search service when relevance or scale demands it
Tenancy, roles and permissionsBuildThey encode your product’s rules
Entitlements and plan limitsBuildThe link between your pricing and your product
Internal admin / back officeBuild plainly, or use an internal-tool builder over a read replicaNeeds knowledge of your domain; must respect audit and access rules
The core workflowBuildThis is what customers pay for

When you buy, check three things: how the price behaves at ten times your current usage, how you would get your data out, and whether the vendor’s data-processing terms fit what you have promised your own customers.

A reference architecture for a first release

  • One TypeScript application (web front end and API), plus a worker process from the same codebase
  • PostgreSQL with a shared schema, tenant ID on every tenant-owned row, row-level security where practical
  • Users, organisations and memberships as separate entities; three fixed roles
  • Managed or library-based authentication with a path to SSO
  • Hosted billing with checkout, customer portal and webhooks; one entitlement function
  • Database-backed job queue with retries and scheduled jobs
  • Transactional email provider with an authenticated domain
  • Object storage for files, with tenant-prefixed paths and signed URLs
  • Error tracking, structured logs, uptime alerts, audit log, product analytics
  • Containers on a managed platform or VM, staging and production, automated deploys, tested backups

That setup will carry most B2B products well past their first few hundred customers, at a running cost that stays small next to development. What each part costs to build is set out in our SaaS development cost breakdown, the order to build it in is covered in how to build a SaaS product, and if you would like a team to build it, see BBR’s SaaS development service.

Questions

Frequently asked
questions.

What is the best tech stack for a SaaS startup?

The one your team knows well, built from mainstream parts: a typed web framework, a relational database, a hosted billing provider, managed or library-based authentication, a job queue and a transactional email service. A common concrete version is TypeScript with React or Next.js, Node.js and PostgreSQL. Products rarely fail because of the language; they fail because of tenancy, billing and scope mistakes.

Which multi-tenancy model should I choose?

A shared database and shared schema with a tenant ID on every row is the right default for most B2B SaaS products. Choose schema-per-tenant or database-per-tenant only for a specific reason, such as contractual isolation, per-customer data residency or very large tenants, and only when the number of tenants is small enough to operate that way.

Should I use microservices for a SaaS product?

Not at the start. A modular monolith is faster to build, simpler to deploy and much easier to debug for a small team. Extract a service when there is a measured reason: a component with a very different scaling profile, a different runtime, or a separate team that needs to deploy independently.

Is Firebase or Supabase enough for a SaaS backend?

For many early products, yes, especially Supabase-style platforms built on PostgreSQL, where your data stays in a standard relational database you can move later. Be careful to enforce tenant isolation and permissions on the server side, not only in client code, and check how the pricing behaves at your expected usage. Document-store backends suit relational business data less well.

How do I handle sales tax and VAT on subscriptions?

There are two routes. Use your billing provider’s tax calculation features and register, file and remit in the places where you have an obligation. Or sell through a merchant of record, which becomes the seller for tax purposes and handles registration and remittance in return for a higher fee. Which is appropriate depends on where you and your customers are, so take advice from an accountant; this is not something to settle in code.

Should I choose a different stack if the product has AI features?

Usually not. Hosted language models are called over an API from whatever backend you have, and PostgreSQL can store embeddings for retrieval. You need a job queue for long-running calls, streaming support in the front end, and per-tenant usage tracking for cost control. See our LLM integration guide.

Your next move

Want a second opinion on your architecture?
Send us the outline.

Describe the product, the customers and any constraints such as data residency or an existing codebase. We reply with questions and a recommended approach.

Discuss your SaaS architecture