
As data teams serve more clients from one platform, building dbt transformations becomes tricky. Should you make separate projects for each client? Copy models with parameters? There is a better way that works well and stays flexible.
The answer is smart multi-tenant dbt packages. These can serve hundreds of brands without getting too complex. Here's how to think about, design, and build a dbt setup that grows well from 10 to 100+ clients.
First, let's see why simple scaling methods fail. Say you serve 60+ clients. Each has different data sources and business rules. If you make separate models for each client, you will face big problems.
Too Many Models: Base zone models grow by client count. What starts as 20-30 models becomes 1,200-1,800 models across staging, intermediate, and base layers.
Hard to Maintain: Every bug fix or business rule change needs manual updates across dozens or hundreds of model files.
Logic Gets Different: When you build the same thing separately for each client, things start to drift. This leads to data quality problems and hard debugging.
Waste of Resources: Compute costs grow with duplicated work, even when clients share the same business logic for 60-80% of their pipeline.
The main pattern for multi-tenant dbt packages is "wide-to-narrow." Instead of splitting data early, keep transformations shared as long as possible. Only split to client-specific logic when you really need to.
Main Rule: Use as much shared transformation logic as possible. Use as little client-specific code as possible.
Here's how to structure your layers:
Shared Layers (Keep Wide):
Client-Specific Layers (Go Narrow):
This pattern lets 60-80% of transformation work be shared across clients. Most data transformation logic is the same across clients, even when final reporting needs differ.
Example: Instead of making stg_facebook_ads_client_a and stg_facebook_ads_client_b, make one stg_facebook_ads model that handles all clients' Facebook data. Then filter or customize only at the final reporting layer.
Building your multi-tenant solution as a dbt package from day one helps long-term scaling. This creates a deployable, testable unit that can be set up for different client environments.
Why Packages Help Multi-Tenancy:
Consistency: Every deployment gets the same transformation logic. No logic drift between clients.
Easy to Maintain: Bug fixes and new features go to all clients through package updates instead of manual model edits.
Easy to Test: Packages can be tested well with mock data before going to production client environments.
Configurable: Variables and parameters allow client-specific behavior without code copying.
Package Design Rules:
Configuration Strategy: Create a standard configuration pattern where new clients can be added by putting their parameters in a config file, without touching the package code.
# dbt_project.yml
vars:
client_configs:
client_a:
data_sources: ["google_ads", "facebook_ads", "linkedin_ads"]
custom_attribution_window: 7
reporting_timezone: "America/New_York"
client_b:
data_sources: ["google_ads", "tiktok_ads"]
custom_attribution_window: 14
reporting_timezone: "America/Los_Angeles"
Multi-tenant setups create unique performance challenges. When you process data for 100+ clients in shared tables, smart partitioning and clustering strategies become key for keeping query performance good and controlling costs.
Partitioning Strategy for Multi-Tenant Tables:
Time-Based Partitioning: Always partition your multi-tenant fact tables by date (usually daily). This makes sure reporting queries only scan relevant time periods, no matter how many clients are in the table.
Client-Based Clustering: Cluster by client ID or brand identifier as your first clustering key. This makes sure client-specific queries only scan data for that client.
Example Table Design:
{{ config(
materialized='incremental',
unique_key=['client_id', 'date', 'campaign_id'],
partition_by={'field': 'date', 'data_type': 'date'},
cluster_by=['client_id', 'channel', 'campaign_type']
) }}
Managing Table Size Growth:
When serving 100+ brands, your tables will grow a lot. Plan for scenarios where daily partitions have 70K-115K rows across all clients. Key strategies include:
Remove Empty Data Early: Remove rows with zero metrics early in your pipeline. Empty impressions, clicks, or spend rows add no value but use storage and compute.
Incremental Processing: Use smart incremental strategies that only process changed data, instead of recomputing entire client datasets.
Smart Materialization: Not every intermediate transformation needs to be materialized. Use CTEs for lightweight transformations and only materialize when performance testing shows it helps.
Not all clients are the same. A good multi-tenant dbt setup must handle different client sizes and complexity needs. The key is designing a system that can serve standard clients well while giving enterprise clients the customization they need.
Tiered Client Strategy:
Standard Clients (80-90% of clients):
Enterprise Clients (10-20% of clients):
Decision Framework for Client Placement:
Consider moving clients to dedicated infrastructure when:
The technical foundation of scalable multi-tenant dbt packages lies in sophisticated parameterization and dynamic model generation. Instead of hard-coding client-specific logic, build a macro-driven system that can adapt behavior based on configuration.
Dynamic Data Source Handling:
Clients will have different combinations of data sources (Google Ads, Facebook, LinkedIn, TikTok, etc.). Design your package to dynamically include only the sources each client actually uses:
-- Example macro for dynamic source union
{% macro union_client_sources(client_id, source_types) %}
{% set unions = [] %}
{% for source_type in source_types %}
{% if source_type in var('client_configs')[client_id]['data_sources'] %}
{% do unions.append("select * from " ~ ref(source_type ~ '_normalized')) %}
{% endif %}
{% endfor %}
{{ unions | join(' union all ') }}
{% endmacro %}
Configuration-Driven Model Behavior:
Use configuration to control model behavior without creating separate model files:
-- Example of configuration-driven transformations
select
client_id,
date,
campaign_id,
impressions,
clicks,
spend,
-- Dynamic attribution window based on client config
{{ attribution_calculation(
window_days=var('client_configs')[var('current_client')]['attribution_window']
) }} as attributed_conversions
from {{ ref('base_campaign_performance') }}
where client_id = '{{ var("current_client") }}'
Environment-Specific Deployment:
Design your package to work across different deployment patterns:
When properly implemented, a multi-tenant dbt package architecture delivers measurable improvements across multiple dimensions:
Cost Efficiency at Scale:
Operational Benefits:
Performance Characteristics:
Plan Multi-Tenancy from the Start: Don't retrofit multi-tenancy onto existing single-client architectures. The foundational decisions around model structure, naming conventions, and configuration patterns are much harder to change later. Design your initial models with the assumption they'll serve multiple clients.
Invest in Package Infrastructure Early: Building proper package foundations—configuration systems, testing frameworks, deployment pipelines—feels like overhead when you have 5 clients. It becomes essential when you have 50. Start with package-first development even if it seems like over-engineering initially.
Performance Testing at Scale: Test your architecture with realistic multi-client data volumes early. A design that works well with 10 clients and 1M rows may break down with 100 clients and 100M rows. Load test your partitioning and clustering strategies before you're forced to optimize in production.
Getting Started: Your Multi-Tenant Implementation Roadmap
Phase 1: Foundation (Months 1-2)
Phase 2: Multi-Client Validation (Months 2-3)
Phase 3: Scale and Optimize (Months 3-6)
Phase 4: Production Hardening (Months 6+)
Multi-tenant dbt architectures provide sustainable competitive advantages. When you can onboard new clients in days rather than weeks, update business logic across hundreds of clients simultaneously, and provide consistent data quality at scale, you've built a platform that can grow with your business rather than constraining it.
The patterns described here provide a framework for building analytics platforms that scale gracefully from 10 to 1000+ clients. With thoughtful upfront design, you can serve hundreds of brands without hundreds of times the complexity.

Many businesses today use different software tools that don't talk to each other. This creates a big problem. You have valuable customer information trapped in one system that could help you in another system. But moving that data by hand takes too much time.