# White label saas architecture

The reseller is worth six figures a year. They want one feature the platform does not have, and they want it next month. A developer opens a branch. It closes the deal, and for a while nobody notices the cost. Eighteen months on, every upstream fix has to be ported by hand across four repositories, and nobody wants to touch the oldest one.

White-label SaaS architecture comes down to five decisions: how tenant data is separated, where tenant identity is resolved in the request pipeline, how custom domains get working TLS without a human involved, how branding is applied without a build step, and how outbound email and billing carry the tenant's identity. Two of those are expensive to reverse. The rest you can change later.

- Start pooled. Shared database, shared schema, row-level security. Dedicated databases are a tier you sell, not a default you assume.
- Resolve tenant identity once, at the edge or in middleware, never inside individual controllers.
- Treat branding as data. Inject CSS custom properties at render time instead of compiling per-tenant stylesheets.
- Automate custom domains behind a verification endpoint, or hand the problem to a managed edge provider.
- Keep one codebase. Every client-specific requirement goes through configuration, feature flags or extension points.

## What searchers usually mean by white-label SaaS architecture

Three ideas get blurred together here, and the architecture changes depending on which one you need.

Multi-tenancy means one system serving many customers. White-labeling means the customer's branding sits on top of that system. Reselling goes further: the tenant has their own end users, their own support relationship and often their own billing.

Most teams searching for this are building all three at once. They need a multi-tenant product that looks like it belongs to the customer and lets that customer serve users of their own. That is the system this guide assumes.

## Decision 1: How tenant data is separated

This is the highest-stakes decision in the build, which is why it goes first. It sets your infrastructure cost, your deployment speed and the shape of every query you write afterwards.

There are three models, and AWS labels them pooled, bridge and silo. Pooled means one database and one schema, with rows tagged by tenant. Bridge means one database with a schema per tenant. Silo means a dedicated database instance per tenant.

Schema-per-tenant is the one that looks clever on a whiteboard. Clean separation, no shared tables, easy to reason about. Then you reach two hundred schemas and a single column addition becomes a rolling migration with its own failure modes and its own runbook. Deployment friction compounds with every tenant you sign.

Start pooled. For early-stage and SMB white-label products it is almost always right: infrastructure stays cheap, and migrations stay atomic.

Security in a pooled model should not depend on developers remembering a `WHERE tenant_id = ?` clause. One forgotten filter in one report query is a cross-tenant data leak. Use PostgreSQL row-level security so the rule lives in the database engine rather than in the application's good intentions. You set the current tenant on the connection, and the database filters everything else out.

One caveat worth knowing before you ship it. If you run a transaction-pooling connection pooler such as PgBouncer, connections are handed to different requests between transactions, so a session-scoped setting can outlive the request that set it. Set the tenant with a transaction-scoped local variable instead, and verify the behaviour against your pooler's documented pooling mode.

Siloed databases stay on the shelf until someone pays for them. Sell isolation as an enterprise tier with a price attached, and build the extraction path when the first customer signs.

## Decision 2: Resolving tenant identity on every request

Most teams make this decision by accident. The first controller that needs to know the tenant does a lookup inline, the next one copies it, and within a year the logic exists in nine places with three subtly different fallbacks.

Resolve it once. The request arrives with a `Host` header, middleware or an edge worker maps that hostname to a tenant, and the tenant lands in the request context before any business logic runs. Keep the domain-to-tenant map in a fast cache such as Redis or edge KV so the lookup does not cost a database round trip on every request.

Carrying that context through the call stack is a language problem, not an architecture problem. Node has `AsyncLocalStorage`, Go has `context.Context`, and most frameworks offer request-scoped dependency injection. Any of them beats threading a tenant ID through every function signature.

The cache brings its own problem: invalidation. When a tenant changes or removes a custom domain, a stale entry either routes traffic to the wrong tenant or drops it. Treat domain changes as an explicit cache-busting event, not something a TTL will quietly fix.

## Decision 3: Custom domains and automatic TLS

Routing happens in two layers. Every tenant gets a subdomain on your platform domain by default, served by wildcard DNS and a wildcard certificate. Vanity domains sit on top as an upgrade.

Subdomain delegation is simple: the customer adds a CNAME record pointing `app.theircompany.com` at your platform. Apex domains are harder, because DNS does not permit a CNAME at the zone root. The customer needs an ALIAS or ANAME record, or their DNS provider's flattening feature. Expect to explain this repeatedly in onboarding docs.

Turning that hostname into a working HTTPS endpoint with nobody on your team involved is the real work. Three approaches dominate.

ApproachMechanismBest forTrade-offs**Managed edge**Cloudflare for SaaS and equivalents handle SNI routing and certificate issuance at the edge.Teams that want zero certificate infrastructure to operate.Vendor dependency, and cost that scales with custom domain count.**Self-hosted proxy**Caddy issues certificates on demand after checking an internal authorization endpoint.Self-hosted stacks and cost-sensitive platforms.You run and monitor the proxy tier, and the authorization endpoint becomes critical infrastructure.**Cloud-native**A CloudFront or equivalent distribution forwards the `Host` header to backend origins.Teams already committed to one cloud.More configuration, and routing often needs custom edge functions.What does not work is binding each customer certificate to a load balancer listener. Cloud load balancers publish a certificate limit per listener, so check your provider's current quota before you plan around it. Either way the model runs out well before a platform with real reseller volume does.

The certificate authority has limits too. Public certificate authorities enforce rate limits on new certificates and new orders, and those limits get revised over time, so read the authority's current published limits rather than a blog post. Design the onboarding flow as a queue with retries and backoff instead of firing an ACME request the instant a tenant saves a domain.

Before issuing anything, prove the tenant controls the hostname. If you run Caddy's on-demand TLS, that means an `ask` endpoint that confirms the domain is registered and verified in your database. Leaving it open is not a configuration preference. It is a vulnerability, covered below.

## Decision 4: Theming without a build step

Per-tenant CSS bundles are the trap here. The moment branding requires a build, onboarding requires a deploy, and your release pipeline becomes a customer-facing bottleneck.

Branding is data. Store the colour palette, typography choices, logo URL and favicon as structured metadata on the tenant record, editable from an admin panel.

At render time, read that metadata and write CSS custom properties into the root of the document, either during server-side rendering or through an edge HTML transformation. Your single stylesheet references the variables. One bundle, any number of brands.

Serve logos and favicons from object storage keyed by tenant. Nothing branded belongs in the application image.

Know where theming stops. Colours, type and assets are configuration. Layout changes, extra screens and bespoke components are not, and trying to express them through a theme system produces a slow, unreadable mess. Use feature flags or slots for those.

One practical constraint: when tenants pick their own colours, some of them will pick unreadable combinations. Run contrast checks on submitted palettes and either warn or clamp, or you will ship accessibility failures on your customers' behalf.

## Decision 5: Outbound identity, email and billing

Teams remember this one after deliverability collapses, usually a week after launch.

Mail sent from a tenant's domain needs SPF, DKIM and DMARC alignment. Without it, messages are rejected or filtered, and the tenant blames your platform rather than their DNS. Mailbox providers have tightened bulk sender requirements in recent years, so check the current published rules for the providers your tenants actually send to.

There are two workable architectures. Generate per-tenant authentication records through your sending provider's API and ask the tenant to add them to their DNS, which keeps sending centralized and observable. Or let tenants bring their own SMTP or API credentials, which shifts deliverability ownership to them and suits larger customers with existing email infrastructure. Plenty of platforms offer both, with the first as the default.

Webhooks deserve the same care. Sign every outbound payload so the receiving system can verify it came from you, and version the signature scheme before you need to change it.

Billing is a commercial question with architectural consequences. If the tenant bills their own end users, decide early who holds the contract with those users, because it determines who handles refunds, chargebacks and tax. Payment platforms publish documented patterns for this, such as Stripe Connect's connected accounts model. Pick one before you write the first invoice.

## Four failure modes worth naming

**The codebase fork.** A high-value reseller asks, a branch appears, and maintenance velocity collapses because every fix now needs porting. One codebase. Customizations go through configuration, feature flags, plugins or webhooks, and if a request genuinely cannot be expressed that way, that is information about the roadmap, not a reason to branch.

**Open on-demand TLS.** An unprotected certificate provisioner lets anyone point an arbitrary domain at your IP and make you request a certificate for it. That burns through CA rate limits, bloats certificate storage and can take the proxy tier down. The authorization endpoint is mandatory, not optional hardening.

**Noisy neighbours.** In a pooled database, one tenant running a large export can degrade latency for every other tenant on the platform. Rate-limit by tenant ID at the gateway, and move heavy work onto queues with per-tenant concurrency caps.

**Duplicate login and marketing pages across dozens of domains.** Search engines handle this through canonicalization, not punishment: when the same page shows up on many hostnames, one version gets picked for indexing, and the choice may not be yours. Control it. Apply `noindex` to shared unauthenticated portal views, or give each tenant distinct metadata and an explicit canonical link.

## What to build first

You do not need all of this on day one. Build it in this order.

1. **Wildcard subdomains and a pooled database with row-level security.** This gets your first tenants live and keeps the data model clean.
2. **Tenant context middleware.** Build it before the second significant feature ships, because retrofitting it means touching every query you have written.
3. **Theming as data.** Add it when the first customer asks for their logo, which will be soon.
4. **Vanity domains and automated TLS.** Build the pipeline when someone will pay for a custom domain, not before.
5. **Dedicated infrastructure.** Offer siloed databases as a priced enterprise tier, with a tested extraction path.

Steps three, four and five are deferrable. Steps one and two are not, because the data model and the tenant resolution layer are the two pieces the rest of the system is built on top of.

## FAQ

**Can I start pooled and move a single tenant to its own database later?**Yes, and it is the standard upgrade path for enterprise customers. You build an extraction process that copies one tenant's rows into a dedicated instance and repoints routing at it. Build and test it when the first customer asks, not speculatively.

**Do I need a wildcard certificate if I use a managed edge provider?**No. Managed edge providers issue and renew certificates for both your platform subdomains and your customers' vanity domains. You still need to decide how tenants prove domain control.

**How do customers point an apex domain at a SaaS platform?**With an ALIAS or ANAME record, or their DNS provider's CNAME flattening feature. A plain CNAME is not valid at the zone apex, which is why apex support varies by registrar.

**What breaks when a tenant changes their custom domain?**Your cached domain-to-tenant mapping goes stale, and requests either route to the wrong tenant or fail. Invalidate the cache entry as part of the domain change itself, and revoke or stop renewing the old certificate.

**Is schema-per-tenant ever the right choice?**Sometimes, usually in enterprise software with low tenant counts and regulatory requirements for logical separation. For high-volume SaaS with hundreds of tenants, the migration overhead outweighs the isolation benefit.

## The two decisions you cannot undo

Most of a white-label platform is reversible. Theming, sending providers and billing plumbing can all be swapped with effort and a migration window. The data model and the place where tenant identity gets resolved cannot, so decide both deliberately and write down why.

Then protect the single codebase, because that is the rule everything else depends on.

[AWcode](https://awcode.com) builds multi-tenant B2B SaaS with billing, roles, APIs and white-label support, and runs its own products on the same patterns. The [development services](https://awcode.com/development) page covers how we work.

```json
{
  "title": "White-Label SaaS Architecture: The Five Decisions That Are Hard to Reverse",
  "image_ideas": [
    {
      "slot": "1",
      "prompt": "Three simple panels: one shared database with mixed-colour rows, one database with separate labelled schema blocks, three separate database cylinders. Same visual language as image one. Short captions under each panel.",
      "alt_text": "Comparison illustration of pooled, schema-per-tenant and database-per-tenant isolation models.",
      "placement": "Decision 1: How tenant data is separated"
    },
    {
      "slot": "2",
      "prompt": "A clean horizontal flow illustration: a browser with an unnamed custom domain in the address bar, an arrow to an edge or proxy layer, a branch to a small cache icon, a branch to a tenant lookup table, then into an application box and a database cylinder. Flat vector style, muted palette, no logos, no real domain names. Use placeholder labels like client-domain.example.",
      "alt_text": "Diagram of a request travelling from a custom domain through an edge proxy and tenant lookup into a shared application and database.",
      "placement": "Decision 2: Resolving tenant identity on every request"
    },
    {
      "slot": "3",
      "prompt": "An abstract split illustration: a configuration panel with colour swatches and a logo placeholder on one side, two differently branded but structurally identical interface mockups on the other. Generic grey-box UI, no recognisable product.",
      "alt_text": "Illustration showing one interface rendered with two different tenant colour schemes and logos from the same configuration.",
      "placement": "Decision 4: Theming without a build step"
    }
  ]
}

```

---

**How this post looks on the live site:** Rendered in a windowed news reader inside the AWcode OS desktop, alongside other posts.

---

**Canonical HTML version:** https://awcode.com/news/white-label-saas-architecture

**About this document:** This is a plain-Markdown mirror of an AWcode.com page, served so that LLMs and agents can read the content without executing the site's retro-OS JavaScript UI. The HTML page at the canonical URL above carries the same content and is also fully indexable.

## Machine-readable

Resources for AI agents, LLMs and integrations:

- [https://awcode.com/llms.txt](https://awcode.com/llms.txt) — index of markdown mirrors
- [https://awcode.com/llms-full.txt](https://awcode.com/llms-full.txt) — every page + post concatenated
- [https://awcode.com/sitemap.xml](https://awcode.com/sitemap.xml) — full sitemap
- [https://awcode.com/robots.txt](https://awcode.com/robots.txt) — crawl + Content-Signal policy
- [https://awcode.com/ai.txt](https://awcode.com/ai.txt) — AI access policy
- [https://awcode.com/openapi.json](https://awcode.com/openapi.json) — OpenAPI 3.1 spec
- [https://awcode.com/.well-known/api-catalog](https://awcode.com/.well-known/api-catalog) — RFC 9264 / 9727 link set
- [https://awcode.com/.well-known/mcp.json](https://awcode.com/.well-known/mcp.json) — MCP discovery
- [https://awcode.com/mcp](https://awcode.com/mcp) — MCP server endpoint (POST JSON-RPC 2.0)
- [https://awcode.com/.well-known/agent-skills/index.json](https://awcode.com/.well-known/agent-skills/index.json) — Agent Skills index

### Public API — concrete examples

- [GET https://awcode.com/api/posts](https://awcode.com/api/posts) — list recent published posts
- [GET https://awcode.com/api/posts/how-startup-studios-de-risk-saas-builds-before-the-first-sprint](https://awcode.com/api/posts/how-startup-studios-de-risk-saas-builds-before-the-first-sprint) — fetch one post
- [GET https://awcode.com/api/pages/about](https://awcode.com/api/pages/about) — fetch the about page

### Markdown mirrors — concrete examples

- [https://awcode.com/index.md](https://awcode.com/index.md) — homepage
- [https://awcode.com/about.md](https://awcode.com/about.md) — about page
- [https://awcode.com/news/how-startup-studios-de-risk-saas-builds-before-the-first-sprint.md](https://awcode.com/news/how-startup-studios-de-risk-saas-builds-before-the-first-sprint.md) — one news post
