dbt Project Folder Structure
A dbt project has a specific folder layout. Every folder has a defined purpose. Understanding this structure helps you know exactly where to put each file and where to look when something goes wrong.
Full Project Tree
my_dbt_project/
├── dbt_project.yml ← Project settings
├── packages.yml ← External packages
├── profiles.yml ← (usually in ~/.dbt/, not here)
│
├── models/ ← Your SQL transformation files
│ ├── staging/
│ │ ├── stg_customers.sql
│ │ ├── stg_orders.sql
│ │ └── schema.yml
│ ├── intermediate/
│ │ └── int_orders_joined.sql
│ └── marts/
│ ├── fct_orders.sql
│ ├── dim_customers.sql
│ └── schema.yml
│
├── tests/ ← Custom (singular) test SQL files
│ └── assert_order_total_positive.sql
│
├── seeds/ ← CSV files to load as tables
│ └── country_codes.csv
│
├── snapshots/ ← SCD Type 2 snapshot models
│ └── orders_snapshot.sql
│
├── macros/ ← Reusable Jinja SQL functions
│ └── cents_to_dollars.sql
│
├── analyses/ ← Ad-hoc SQL not run as models
│ └── revenue_deep_dive.sql
│
├── logs/ ← dbt run logs (auto-generated)
│ └── dbt.log
│
└── target/ ← Compiled SQL and run results
├── compiled/
├── run/
├── manifest.json
└── run_results.json
dbt_project.yml
This is the brain of your project. It tells dbt the project name, which profile to use for database connection, where to find each type of file, and what default settings to apply to models.
name: 'my_dbt_project'
profile: 'my_warehouse'
models:
my_dbt_project:
staging:
+materialized: view
marts:
+materialized: table
The indented model config lets you set materializations per folder. All models in staging/ become views. All models in marts/ become tables. Individual models can override these defaults.
The models/ Folder
Every .sql file inside models/ becomes a dbt model — a table or view in your warehouse. You can organize models into subfolders. dbt searches all subfolders recursively.
Recommended Layer Structure
models/ staging/ ← Clean and rename raw source columns (1:1 with source tables) intermediate/ ← Join staging models together, apply business logic marts/ ← Final tables used by dashboards and analysts
Think of it like an assembly line:
Raw Source Data
|
v
[staging] — wash and label the parts
|
v
[intermediate] — assemble the parts
|
v
[marts] — deliver the finished product
The schema.yml Files
schema.yml files live inside the models/ folder (usually one per subfolder). They contain model descriptions, column descriptions, and test definitions. You can name these files anything ending in .yml, but schema.yml is the convention.
# models/staging/schema.yml
version: 2
models:
- name: stg_customers
description: "One row per customer from the raw CRM export"
columns:
- name: customer_id
description: "Primary key"
tests:
- not_null
- unique
- name: email
description: "Customer email address"
tests:
- not_null
The seeds/ Folder
Seeds are CSV files that dbt loads into your warehouse as tables. Use seeds for small reference datasets that change rarely, such as:
- Country code to country name mappings
- Product category labels
- Region definitions
seeds/country_codes.csv: code,name US,United States IN,India GB,United Kingdom DE,Germany
Run dbt seed to load this CSV into a table called country_codes in your schema.
The snapshots/ Folder
Snapshot models track how a record changes over time. If a customer changes their email address, a normal model overwrites the old email. A snapshot keeps both the old and new values with timestamps. Snapshots are covered in detail in a later topic.
The macros/ Folder
Macros are reusable pieces of Jinja SQL. Write a macro once and call it from any model. For example, a macro that converts cents to dollars:
-- macros/cents_to_dollars.sql
{% macro cents_to_dollars(column_name) %}
({{ column_name }} / 100.0)::numeric(10,2)
{% endmacro %}
Use it in any model: {{ cents_to_dollars('price_cents') }}
The tests/ Folder
This folder holds singular tests — custom SQL queries where any returned row represents a data quality failure. Generic tests (not_null, unique) go in schema.yml. Complex tests with custom logic go here as .sql files.
The analyses/ Folder
SQL files in analyses/ are compiled by dbt but never executed as models. Use this folder for ad-hoc queries you want to version-control alongside your models without creating warehouse objects.
The target/ Folder
dbt generates this folder automatically every time you run a command. You should add it to your .gitignore file so it does not end up in version control.
# .gitignore target/ dbt_packages/ logs/
The logs/ Folder
dbt writes a detailed text log of every command you run to logs/dbt.log. When a model fails and the terminal output is not detailed enough, open this file for the full error message and stack trace.
packages.yml
This file lists external dbt packages your project depends on. Packages from the dbt Hub provide pre-built macros and tests you can use without writing them yourself.
packages:
- package: dbt-labs/dbt_utils
version: 1.1.1
Run dbt deps after creating or updating this file to download the packages into a dbt_packages/ folder.
Quick Reference: Where Does Each File Go?
File Type Folder ----------------------- --------- SQL transformation models/ Data quality YAML config models/ (schema.yml) CSV reference data seeds/ Time-tracking models snapshots/ Reusable SQL functions macros/ Ad-hoc queries analyses/ Custom SQL tests tests/ Package list packages.yml (project root) Project config dbt_project.yml (project root)
