The Complete Guide to dbt™ Database Configuration
Master dbt™ database configuration: understand cross-database writes, prevent dev/prod conflicts, and configure databases properly across environments.

Fabio Di Leta
Nov 21, 2025
·
6
min read
dbt™ database configuration looks simpler than schemas. It's not. You can accidentally write to production from dev, break permissions, or deploy code that works in CI but fails in prod.
This guide fixes that.
How dbt™ Database Configuration Works
Start in profiles.yml:
Run dbt run --target dev and every model lands in analytics_dev by default.
That's your base database. Everything else builds from here.
Target Database vs Custom Database
Two different things. Here's what they actually are.
Target database: Where dbt™ puts your models by default. Set in profiles.yml:
Run dbt run --target dev and every model lands in analytics_dev. That's it.
Custom database: Your override. Set in the model:
When you set a custom database, dbt™ uses it directly. No concatenation. No prefix. Just switches databases.
Example:
Target database:
analytics_devCustom database:
data_scienceWhere the model lands:
data_science.dbt_john.my_model
Not analytics_dev_data_science. Just data_science.
Why no concatenation? Because database names need to be exact. You can't just make up database names. They have to exist and you need grants on them.
But this creates a problem. A big one.
The Problem with Custom Databases
Your model has database='data_science' in the config. Works in prod. Fails in dev.
Why? Your dev user doesn't have write access to data_science. Or worse - you accidentally write to the production data_science database from your laptop.
Both bad.
Here's what happens by default:
Environment | Target Database | Custom Database Config | Where Model Lands |
|---|---|---|---|
Production |
|
|
|
Developer - John |
|
|
|
Developer - Jane |
|
|
|
See it? Everyone hits the same data_science database. If it's the prod database, you just wrote dev data to prod. If it's a database you don't have access to, your run fails.
You need environment-specific logic.
Understanding Database Behavior: No Concatenation
Unlike schemas, databases don't concatenate. The generate_database_name macro works differently:
No underscore. No concatenation. If you set a custom database, dbt™ uses it directly.
This is why you need to override it.
Controlling Database Behavior: Development vs Production
Production needs cross-database writes. Dev doesn't (and shouldn't).
Create macros/get_custom_database.sql:
Now:
Production:
database='data_science'→data_scienceDev:
database='data_science'→analytics_dev(ignores config)
Developers can't accidentally write to prod databases. Config stays the same across environments. Problem solved.
Database Configuration Priority
Four places to set databases. 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 Database
From profiles.yml. Your base database. Used when nothing else is set.
Common Database Patterns
Layered by Database
Regional Separation
Dev Prefixing Pattern
Want to track costs by team but keep everything in dev?
Results:
Prod:
database='data_science'→data_scienceDev:
database='data_science'→analytics_dev_data_science
All dev databases clearly marked. Structure mirrors prod.
Why Cross-Database Writes?
Common scenarios:
Data science sandboxes: Your data science team needs tables in their own database for experiments and model training.
Regional isolation: EU data in analytics_eu, US data in analytics_us for compliance.
Access control: Sensitive data in secure_analytics, everything else in analytics.
Cost tracking: Separate databases for different departments to track spend.
Source system alignment: Keep staging close to source systems in their native databases.
Debugging Database Issues
See where models compile
Full database.schema.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.
Test permissions
Best Practices
Override generate_database_name for dev environments. The default behavior is dangerous.
Production should allow custom databases. Dev should ignore them or prefix them.
Document which databases exist and what they're for. Drop a note in the project README.
Use database configs sparingly. Most projects don't need them. Schemas are usually enough.
What Not to Do
Don't Allow Cross-Database Writes in Dev
Some people want feature parity between dev and prod:
Bad idea. Here's what happens:
Environment | Target Database | Custom Database | Result |
|---|---|---|---|
Production |
|
|
|
Developer - John |
|
|
|
Developer - Jane |
|
|
|
CI PR 123 |
|
|
|
Everyone writes to the same database. Developer 1 and Developer 2 overwrite each other. CI builds collide. If data_science is the prod database, you just mixed dev data into prod.
Fix: Block custom databases in dev.
Production gets cross-database writes. Dev stays isolated. Problem solved.
Don't Skip the Macro Override
"I'll just be careful" doesn't work. Someone will run dbt run against the wrong target. Someone will forget to check their active profile. Someone will deploy code without testing.
The macro is your safety net. Use it.
Key Takeaways
Default behavior: dbt™ uses custom databases directly. No concatenation. This is dangerous in dev.
Override
generate_database_nameto control database behavior by environment.
Production gets cross-database writes. Dev ignores custom databases or prefixes them.
Configure at model, directory, or project level. Most specific wins.
Test permissions before deploying. Grant failures are common.





