# Per-Tenant Configuration and Feature Flag Strategy in Multi-Tenant SaaS

> An advanced guide to designing per-tenant configuration and feature flag systems in multi-tenant SaaS, covering storage patterns, rollout strategies, and operational pitfalls.

- **Published:** 2025-03-21
- **Author:** Clixo
- **Reading time:** 7 min read
- **Tags:** per-tenant-configuration, feature-flags, multi-tenant, saas-architecture, advanced-guide
- **Canonical URL:** https://clixo.sh/blog/per-tenant-configuration-feature-flags-multi-tenant-saas

Your multi-tenant SaaS product ships a new feature. Some tenants need it enabled immediately. One enterprise customer wants to evaluate it in their staging environment for three weeks before enabling it in production. A regulated-industry customer cannot enable it at all until their compliance team reviews it. And you need all of this to be configurable without a code deployment.

This is the standard feature rollout problem in multi-tenant SaaS, and per-tenant configuration is the mechanism that solves it. But the design of that configuration system — how it is stored, resolved, cached, and changed — determines whether it scales cleanly or becomes a maintenance liability.

## What Per-Tenant Configuration Needs to Do

A per-tenant configuration system must handle at least three categories of data:

**Feature flags**: Boolean or multi-variant flags that control whether a feature is available to a tenant. Flags can be on/off or can specify which variant of a feature a tenant sees.

**Plan limits and quotas**: Numerical limits tied to the tenant's plan — API rate limits, seat counts, storage caps, monthly usage quotas. These change when a tenant upgrades or downgrades.

**Behavioral configuration**: Settings that change how the product behaves for a tenant — enabled integrations, notification preferences, custom field definitions, branding options, locale and timezone settings.

These categories have different change frequencies and different read patterns, which affects how they should be stored and cached.

## Storage Patterns for Per-Tenant Configuration

### Tenant Configuration Table

The simplest approach: a `tenant_configs` table with a row per tenant and columns for each configuration value. Works well when the set of configurable options is small and stable.

```sql
CREATE TABLE tenant_configs (
  tenant_id    uuid PRIMARY KEY REFERENCES tenants(id),
  plan_tier    text NOT NULL DEFAULT 'standard',
  api_rate_limit integer NOT NULL DEFAULT 1000,
  max_seats    integer NOT NULL DEFAULT 5,
  feature_flags jsonb NOT NULL DEFAULT '{}'
);
```

Using `jsonb` for `feature_flags` lets you add new flags without schema migrations. Query specific flags with the `->>` operator.

**Trade-off:** Every new configuration option requires either a schema migration (for typed columns) or careful key management (for the JSONB blob). Querying across tenants for "all tenants with flag X enabled" requires a JSONB index or a full table scan.

### Key-Value Configuration Store

A separate `tenant_settings` table with rows per configuration key:

```sql
CREATE TABLE tenant_settings (
  tenant_id    uuid NOT NULL,
  key          text NOT NULL,
  value        jsonb NOT NULL,
  updated_at   timestamptz NOT NULL DEFAULT now(),
  PRIMARY KEY (tenant_id, key)
);
```

This is more flexible — adding a new configuration option requires no schema changes — and it makes "which tenants have setting X" queries efficient with an index on `key`.

**Trade-off:** Loading all settings for a tenant requires a query that fetches many rows and assembles them in application code. Partial updates are simpler; full replacements require deleting and reinserting.

### External Feature Flag Service

Tools like LaunchDarkly, Flagsmith (open source), and Unleash provide per-tenant (per-context) flag evaluation with built-in targeting rules, audit logs, and rollout percentages. They offload the infrastructure concern entirely.

**Trade-off:** External services introduce a latency dependency on the critical path if flags are evaluated per-request without caching. The SDK evaluation model (flags evaluated locally from a synced ruleset) avoids this, but requires keeping the local ruleset current.

## Resolving Per-Tenant Configuration at Request Time

The resolution order matters. Configuration values may exist at multiple levels, and the system needs a clear hierarchy:

1. **Per-tenant override** (explicit setting for this tenant).
2. **Plan-level default** (default for the tenant's plan tier).
3. **System default** (baseline default for all tenants).

The resolver reads from top to bottom and returns the first value found. This model lets you set plan-level defaults without touching every tenant's individual settings, and override at the tenant level for specific cases.

Cache the resolved configuration for each tenant. The configuration is usually read far more often than it changes. An in-memory cache with a short TTL (30–60 seconds) reduces database load significantly while keeping changes propagating quickly. For real-time propagation, use a pub/sub channel (Redis pub/sub, for example) to invalidate the cache when a specific tenant's configuration changes.

## Per-Tenant Feature Flags: Advanced Rollout Patterns

### Staged Tenant Rollout

Do not roll out a new feature to all tenants simultaneously. The staged rollout pattern:

1. Enable for internal Clixo/your-company tenant first (dogfooding).
2. Enable for a small set of beta tenants who have opted in.
3. Enable for a percentage of trial and standard tenants.
4. Enable by default for all new tenants.
5. Enable for all existing tenants.
6. Remove the flag after rollout is complete.

Each stage is a configuration change, not a deployment. The feature is already deployed; the flag controls who sees it.

```mermaid
flowchart TD
  S1["1. Enable for internal team (dogfooding)"] --> S2["2. Enable for beta opt-in tenants"]
  S2 --> S3["3. Enable for a percentage of trial tenants"]
  S3 --> S4["4. Default on for all new tenants"]
  S4 --> S5["5. Enable for all existing tenants"]
  S5 --> S6["6. Remove flag from codebase"]
```

### Enterprise-Specific Release Cadence

Enterprise tenants often have change management processes. A new feature that ships in your October release may not be enabled for a regulated-industry enterprise customer until their internal review completes in December.

Design your flag system to support this. The enterprise tenant's feature flag remains disabled after your general rollout. An explicit action (their admin enabling it, or your team enabling it at their request) turns it on for them specifically. The flag has no dependency on the global rollout state.

### Per-Tenant Variants for A/B Testing

Some products run A/B tests scoped to individual tenants — not across all users in a test cohort, but across all users within a specific tenant's account. This is useful for enterprise customers who want to evaluate new UX before enabling it company-wide.

Implement this as a multi-variant flag where the value is the variant name. Resolve the variant from the tenant's configuration and apply it to all sessions within that tenant.

## Operational Pitfalls in Per-Tenant Configuration

**Stale cache during incident response.** When a tenant reports a problem and you need to immediately disable a feature for them, a 60-second cache TTL means the change takes 60 seconds to propagate. For critical flags (kill switches), use a shorter TTL or a cache invalidation mechanism with zero delay.

**Flag accumulation.** Flags that were added for a rollout two years ago and never removed add cognitive overhead and testing surface area. Set a policy: flags have a retirement date, and retired flags are removed from code and configuration within one sprint of the rollout completing.

**Missing audit trail.** Every configuration change for a tenant should be logged: who changed it, what was changed, from what value, to what value, and when. Without this log, debugging unexpected behavior after a configuration change is significantly harder.

**Provisioning gaps.** When a new tenant is created, their configuration must be seeded correctly. A tenant created with a missing configuration key will fall through to the wrong default or throw an error when the application tries to resolve it. Treat configuration seeding as part of the provisioning step, not an afterthought.

Per-tenant configuration is infrastructure, not a feature. Treat it with the same rigor — testing, documentation, auditability — that you would apply to the database layer.

If you are designing a multi-tenant SaaS system and need the per-tenant configuration architecture to work correctly from the start, [Clixo](https://clixo.sh/#contact) builds these systems for product teams who cannot afford to retrofit them later.

---

Clixo · 1141 W Bryn Mawr Ave, Itasca, IL 60143, US · [hello@clixo.sh](mailto:hello@clixo.sh)
[Start a build](https://clixo.sh/#contact) · [All services](https://clixo.sh/services) · [Agent guide (llms.txt)](https://clixo.sh/llms.txt)
