DEV Community

WizCodes
WizCodes

Posted on Originally published at wizcodes.site

How to Add White Label Features: The Tenant Decision

Most teams add white labeling after the product is built. Then they discover their tenant model cannot support it.

White labeling is a data boundary decision. It touches every query, permission check, and background job. Get the tenant structure right from the start or plan to refactor the entire stack later.

What white labeling actually means for your data model

White labeling touches every table that stores user-generated content or configuration.

Add reseller portals and you are deciding whether users, projects, settings, and files belong to one tenant or many. That decision cascades through foreign keys, indexes, and every query that fetches data.

The simplest approach is a tenant_id column in each table. Every query filters by it. Every insert writes it. Your structuring user permissions logic now runs per-tenant instead of globally.

Three patterns we see:

  • Row-level: One database, tenant_id everywhere. Fast to deploy, harder to isolate if one tenant's data grows large.
  • Schema-level: One database, separate schema per tenant. Cleaner boundaries, migration scripts run N times.
  • Database-level: Separate database per tenant. Full isolation, higher infrastructure cost.

Most teams start with row-level. They move to schema or database separation only when a specific tenant hits scale limits or asks for dedicated infrastructure. The migration is straightforward if your queries already filter by tenant from day one.

The tenant boundary questions to settle first

Before you write a line of white label code, decide what kind of separation your customers actually need.

Three customers asking for "branded access" might mean three different architectures.

Do they want cosmetic branding or true data isolation? A customer who needs their logo and color scheme lives in your theme layer. A customer who cannot see another customer's data - ever - needs tenant-level isolation in the database. Those are different builds.

Are you selling to end users or to resellers? If a reseller brings their own customers, you need a hierarchical tenant model - parent tenant owns child tenants, permissions inherit down. Different permission boundary than flat isolation.

Can one user belong to multiple tenants? If yes, your session logic gets complicated fast. Decide this before adding multi-tenancy, because changing it later means rewriting auth.

Most teams pick their model by guessing what sounds right. Better move: map the actual customer request to the lightest architecture that satisfies it. Cosmetic branding does not need tenant isolation. Reseller hierarchies do.

How to structure white label features in practice

We enforce the tenant boundary at three layers.

The presentation layer holds the branding - custom domains, color schemes, logos. Each tenant gets a subdomain or mapped domain that loads their theme from a config table.

The permission layer scopes every access check to a tenant ID. When a user logs in, their session carries the tenant context. Every query, every route, every API call checks that context before returning data.

The data layer enforces isolation at the row level. Every table that holds tenant-specific records includes a tenant ID column. Database policies or application-level filters ensure queries only touch rows that match the session's tenant.

This structure lets resellers customize what their customers see without touching code. When we built the SaaS platforms for clients with reseller models, the three-layer split kept tenant data isolated even when one customer had admin access to their own portal.

Start at the data layer. If isolation breaks there, the other two layers mean nothing.

What to check before you rebuild your permissions

Most teams rebuild their permission system twice. Once when they add white labeling. Again when they realize the first rebuild locked them into the wrong tenant model.

Before you touch a single permission check, map three things:

Who needs to define roles. If your resellers will create their own user types and access rules, your permission system needs to expose that as a configurable boundary. If you define all roles centrally and just toggle features per tenant, the rebuild is smaller.

What crosses tenant lines. Support agents reading cross-tenant logs. Billing systems aggregating usage. Compliance reports that span all customers. Mark those exceptions now or you will carve holes in your schema later.

Where enforcement happens. Row-level security in Postgres enforces isolation at query time. Application-layer checks let you audit every access decision but double the surface for permission bugs. Pick one enforcement layer and commit to it before the rebuild starts.

We have rebuilt permission systems for two white label platforms. Both times, the second question surfaced edge cases the first pass missed. If you are not sure which checks to preserve and which to replace, request a prototype and we will map the boundaries with you before any rebuild begins.

How to tell the architecture will scale

Your architecture is ready to scale when you can add a tenant without touching the application code.

Tenant boundaries are enforced at the data layer. Not scattered across route handlers and business logic.

Three checks tell you the system is structurally sound:

  • Query isolation: Every query includes the tenant filter automatically, enforced by the ORM or database policy - not by remembering to add WHERE tenant_id = ? in each controller.
  • Permission inheritance: A new tenant role gets its permissions from a template, not from copying fifty rows and hoping you did not miss one.
  • Deployment independence: Turning on white label for one tenant does not require a feature flag, a config change, or a code push for the others.

Still adding conditional logic every time a tenant wants custom branding or a different permission set? The architecture is not ready. That pattern collapses under ten white label customers.

Top comments (0)