The Complete Guide to dbt™ Schema Configuration
Everything you need to know about dbt™ schemas: how they work, why concatenation happens, and how to configure them properly for dev and prod.

Fabio Di Leta
Nov 20, 2025
·
5
min read
dbt™ schema configuration is straightforward once you understand the mental model. This guide covers everything: how schemas work, where models land, why concatenation happens, and how to control it all.
How dbt™ Schema Configuration Works
Start in profiles.yml. This is where you define your target schemas:
Run dbt run --target dev and every model lands in analytics_dev.dbt_john by default.
That's your base schema. Everything else builds from here.
Target Schema vs Custom Schema
Two different things. Here's what they actually are.
Target schema: Where dbt™ puts your models by default. Set in profiles.yml:
Run dbt run --target dev and every model lands in dbt_john. That's it.
Custom schema: Your override. Set in the model or in other parts of your dbt™ project:
When you set a custom schema, dbt™ doesn't just use it. It concatenates it with your target schema.
Example:
Target schema:
dbt_johnCustom schema:
stagingWhere the model lands:
dbt_john_staging
Not staging. dbt_john_staging.
Why? So five developers can work on the same project without destroying each other's work. You get dbt_john_staging, Jane gets dbt_jane_staging. Nobody collides.
In production, you want clean names. Override the macro to drop concatenation for prod targets only.
Understanding Schema Concatenation
Add a custom schema to your model:
You expect: analytics_dev.staging.my_model
You get: analytics_dev.dbt_john_staging.my_model
This is the generate_schema_name macro at work. Default behavior:
That underscore in {{ default_schema }}_{{ custom_schema_name }} creates the concatenation.
Why? Developer isolation. Five people working on the same project need their own space. Jane gets dbt_jane_staging, you get dbt_john_staging. Nobody collides.
Controlling Schema Behavior: Development vs Production
Production needs clean schemas. staging, not analytics_staging. Override the macro.
Create macros/get_custom_schema.sql:
Now:
Production:
schema='staging'→stagingDev:
schema='staging'→dbt_john_staging
Done.
Schema Configuration Priority
Four places to set schemas. Most specific wins.
1. Model Config Block
Highest priority. Overrides everything.
2. dbt_project.yml
Directory-level configs. Applies to all models in the tree.
3. schema.yml
Model properties. Lower priority than config blocks.
4. Target Schema
From profiles.yml. Your base schema. Used when nothing else is set.
Common Schema Patterns
Layered Architecture
Multiple Production Targets
No Concatenation Ever
Solo developer? Use this.
Debugging Schema Issues
See where models compile
Full schema.database.table reference is right there in the compiled SQL.
List models with locations
Debug the macro
Run dbt compile and watch what values get passed in.
Best Practices
Override generate_schema_name for production. The default concatenation is for dev isolation only.
Pick a schema naming pattern and stick with it. Staging/intermediate/marts or bronze/silver/gold - doesn't matter which. Consistency matters.
Set schemas at directory level in dbt_project.yml. Less config duplication.
Test in dev first. Run dbt compile and verify models land where you expect before deploying.
Document your pattern. Drop a note in the project README for new developers.
What Not to Do
Don't Remove Concatenation Everywhere
Some people hate the concatenation and do this:
Works great in production. Catastrophic in shared dev/CI environments.
Here's what happens with a model called my_model and schema='marketing':
Environment | Target Schema | Result |
|---|---|---|
Production |
|
|
Developer - John |
|
|
Developer - Jane |
|
|
CI PR 123 |
|
|
CI PR 234 |
|
|
See the problem? Everyone writes to the same schema. John and Jane overwrite each other's work. PR builds collide. Nobody knows whose version is currently deployed.
Fix: Only drop concatenation in production.
Production gets clean schemas. Everyone else gets their own namespace. Problem solved.
Key Takeaways
Default behavior: dbt™ concatenates your target schema with custom schemas. That's for dev isolation.
Override
generate_schema_nameto control schema behavior by environment.Production gets clean schema names. Dev gets concatenated names. Everyone stays in their lane.
Configure at model, directory, or project level. Most specific wins.
Test with
dbt compilebefore deploying.





