Module 1: Your First Dbt Project

Anatomy of a dbt project

Description

A dbt project is, in its simplest form, a folder with two required files and a subdirectory: dbt_project.yml (the project's configuration: its name, where to look for each type of file), profiles.yml (how to connect to the warehouse — you build it in lesson 6, not this one) and models/ (the directory where your .sql files are going to live, still empty at the end of this lesson). Everything else you're going to see in a real dbt project — seeds/, snapshots/, macros/, tests/, target/, logs/ — is either optional until you need it, or is generated by dbt automatically and you never write it by hand.

This lesson builds those two required pieces that really are written by hand — dbt_project.yml and the models/ folder — for Kiosko's project, and uses dbt debug to demonstrate, with real evidence, that dbt already recognizes the project as valid even before profiles.yml exists. That distinction — what it takes for dbt to understand your project, versus what it takes for dbt to connect to a warehouse — is exactly the boundary between this lesson and the next.

Connection to the module. Lesson 4 gave you the dbt CLI installed and working. This lesson gives you the folder that CLI is going to read from here on — kiosko_analytics/, the project you're going to keep building in every module of this guide, all the way to module 8's capstone.

An analogy: a house's blueprints, before a single brick is laid

Before building anything, an architect delivers a blueprint: a document that says what the project is called, how many floors it has, where each room goes — without a single wall existing yet. That blueprint doesn't connect the house to the power grid or to water (that's a separate process, with its own paperwork), but without the blueprint, no contractor would know where to start or what to build in each space.

dbt_project.yml is that blueprint. It declares the project's name, which folder the models go in (models/), which folder the tests go in, the macros, the snapshots — the general shape of the house, before a single .sql file exists inside it. profiles.yml, which you build in the next lesson, is the paperwork for connecting to the power grid and water: without it, the house has a perfectly valid shape, but no power or running water yet. An architect can show you a complete, correct blueprint without the house being connected to any utility — and that, precisely, is what you're going to verify at the end of this lesson with dbt debug.

Worked example: creating kiosko_analytics/, piece by piece

First, the project folder and its one required subdirectory for this module:

mkdir -p kiosko_analytics/models
cd kiosko_analytics

Now, dbt_project.yml — the file that turns that folder into a recognizable dbt project:

# dbt_project.yml
name: "kiosko_analytics"
version: "1.0.0"
config-version: 2

profile: "kiosko_analytics"

model-paths: ["models"]
seed-paths: ["seeds"]
test-paths: ["tests"]
macro-paths: ["macros"]
snapshot-paths: ["snapshots"]

target-path: "target"
clean-targets:
  - "target"
  - "dbt_packages"

models:
  kiosko_analytics:
    +materialized: view

Look carefully at three fields, because each one answers a different question:

  • name: "kiosko_analytics" — the project's internal name. It's the identifier you're going to use later when another dbt project needs to reference this one (outside this guide's scope), and it is, by convention — not by technical requirement — the same text as the folder name.
  • profile: "kiosko_analytics" — the name of the connection profile this project is going to look for inside profiles.yml. It doesn't have to match name — they're two independent fields that, in this project, we decided should share the same text for clarity. Lesson 6 creates profiles.yml with an entry named exactly kiosko_analytics so this field finds its match.
  • models: kiosko_analytics: +materialized: view — the default materialization for every model in this project. view is already, out of the box, dbt's default even if you don't declare it — we leave it explicit here so the project's anatomy doesn't depend on you remembering a default from memory. You're going to get to know exactly what view (versus table) means in module 3.

Diagram: the complete anatomy of a dbt project

kiosko_analytics/
├── dbt_project.yml      <- REQUIRED, you write it by hand (this lesson)
├── profiles.yml          <- REQUIRED to run anything, you write it by hand (lesson 6)
├── models/                <- REQUIRED as a folder, you create it now, empty
│   └── (your .sql files go here, starting in lesson 7)
├── seeds/                 <- optional: small CSVs dbt can load (not in M1)
├── snapshots/              <- optional: automatic SCD type 2 (module 5)
├── macros/                  <- optional: reusable Jinja (module 7)
├── tests/                    <- optional: singular tests (module 4)
├── target/                    <- GENERATED by dbt, never write it by hand
│   ├── compiled/                (the final SQL, no Jinja, that dbt ran)
│   ├── run/                      (a copy of what ran against the warehouse)
│   └── manifest.json              (the project's full graph -- module 7)
└── logs/                        <- GENERATED by dbt, the history of every command

Only three things matter for closing out this module: dbt_project.yml, profiles.yml (lesson 6), and models/. The rest of the list — seeds/, snapshots/, macros/, tests/ — exists so you recognize them when they show up, each in its own module; creating those empty folders today breaks nothing, but isn't necessary yet either. You never touch target/ and logs/ by hand — dbt creates and rewrites them on its own, every time you run a command.

Worked example (continued): dbt debug before profiles.yml exists

With dbt_project.yml already written and models/ already created (still empty), run dbt's first diagnostic command:

dbt debug

What to expect. Without profiles.yml yet, the output is exactly this (the path names reflect wherever you created your own folder):

Running with dbt=1.12.2
dbt version: 1.12.2
python version: 3.12.3
os info: macOS-15.0.1-arm64-arm-64bit
Using profiles dir at /path/to/your/kiosko_analytics
Using profiles.yml file at /path/to/your/kiosko_analytics/profiles.yml
Using dbt_project.yml file at /path/to/your/kiosko_analytics/dbt_project.yml
Configuration:
  profiles.yml file [ERROR not found]
  dbt_project.yml file [OK found and valid]
Required dependencies:
 - git [OK found]

Connection test skipped since no profile was found
1 check failed:
dbt looked for a profiles.yml file in /path/to/your/kiosko_analytics/profiles.yml, but did
not find one. For more information on configuring your profile, consult the
documentation:

https://docs.getdbt.com/docs/configure-your-profile

Read this result carefully, because it confirms exactly what the blueprint analogy says: dbt_project.yml file [OK found and valid] — dbt parsed your YAML, confirmed name, profile, and the paths are well formed, and found no syntax error. profiles.yml file [ERROR not found] — because, on purpose, you haven't created it yet; that's lesson 6's job. The full diagnostic ends with 1 check failed, and the error message even tells you, literally, the exact path where it looked for the missing file and links you to the official documentation. None of this is a mistake on your part at this point in the lesson — it's exactly what's expected to be seen at this point in the module, and it's proof that dbt_project.yml is already complete and correct.

Going deeper: three names that look like one, and aren't

It's worth pausing here because it's this module's most common source of confusion. In Kiosko's project, three different things share, on purpose, the same text — kiosko_analytics — but each lives in a different place and answers a different question:

NameWhere it livesWhat question it answers
The folder nameThe file system (kiosko_analytics/)"Where is this project on my disk?"
name in dbt_project.ymlInside the configuration file"What is this dbt project called internally?"
profile in dbt_project.yml, and the top-level key in profiles.ymlBoth files, and they must match each other exactly"Which connection is this project supposed to run with?"

The only pair that must match exactly, character for character, is the second one with profiles.yml: the value of profile: in dbt_project.yml has to exist as a top-level key inside profiles.yml, or dbt won't find any connection configured for your project (you're going to see the exact error for this case in lesson 6). The folder name, on the other hand, is completely free — you could rename the folder to my_kiosko_project/ and the project would keep working identically, because dbt never reads the folder name for anything.

Common mistakes

Creating models/ with capital letters or a different name than what's declared in model-paths. What happens: someone creates a folder called Models/ or sql/ instead of models/, without noticing dbt_project.yml still points to ["models"]. Why it happens: on some file systems (macOS, by default) folder names aren't case-sensitive when browsing manually, so the mistake doesn't show up until dbt looks for files and finds none. How to spot it: if you run dbt run later and see Found 0 models, even though you have .sql files saved, check that the folder is named exactly what model-paths says in dbt_project.yml. How to fix it: use mkdir -p models exactly as-is, lowercase, like the worked example does — or, if you prefer a different folder name, update model-paths in dbt_project.yml to match.

Assuming that dbt debug with one check in ERROR means everything is broken. What happens: someone sees 1 check failed in this lesson's dbt debug output and concludes they did something wrong in dbt_project.yml. Why it happens: the word "failed" sounds serious, and it's easy not to read carefully which of the two checks failed. How to spot it: always look at the specific line — at this point in the module, dbt_project.yml file [OK found and valid] should be green/OK, and only profiles.yml file [ERROR not found] should fail. How to fix it: if the one that fails is dbt_project.yml, you do have a YAML syntax problem to fix now; if the only one that fails is profiles.yml, you're exactly where this lesson expects you to be — continue to lesson 6.

Editing target-path or clean-targets without understanding they're generated folders. What happens: someone manually copies files into target/, thinking it's part of the project's source code. Why it happens: target/ shows up in the project directory just like models/, and nothing in the file system visually signals that one is generated and the other isn't. How to spot it: if you delete the target/ folder entirely and run dbt run, dbt recreates it from scratch with no problem — that's proof you should never have stored anything important there. How to fix it: treat target/ and logs/ like a Python __pycache__ folder — they regenerate on their own, never edit them by hand, and (as you're going to do in lesson 8) add them to .gitignore.

Exercises

Exercise 1 — Find the bug in a broken dbt_project.yml. This file has a problem dbt debug would catch right away. Without running the command, identify it:

name: "kiosko_analytics"
version: "1.0.0"
config-version: 2
profile: "kiosko_warehouse"
model-paths: ["models"]
See solution

The problem isn't in this file's syntax — it's valid YAML, and dbt debug would mark dbt_project.yml file [OK found and valid] with no problem. The problem is in the value of profile: "kiosko_warehouse": if profiles.yml has its top-level entry named kiosko_analytics (as lesson 6 builds it), this project is never going to find it, because it's looking for one named kiosko_warehouse. The error wouldn't show up in the dbt_project.yml check — that file is valid on its own — but later, when attempting the real connection, with a message along the lines of Could not find profile named 'kiosko_warehouse'.

Exercise 2 — Rename the folder and confirm nothing breaks. After completing this lesson's worked example, rename the project folder from kiosko_analytics/ to proyecto_kiosko/ (without touching any file inside it) and run dbt debug again from inside that renamed folder.

See solution

dbt debug keeps showing exactly the same result as in the worked example — dbt_project.yml file [OK found and valid], profiles.yml file [ERROR not found] — except the absolute paths it prints now say proyecto_kiosko/ instead of kiosko_analytics/. This confirms this lesson's "going deeper" section: the folder name on the file system is completely independent of the name field inside dbt_project.yml — dbt never reads or depends on the name of the folder that contains it. Rename the folder back to kiosko_analytics/ before continuing, to stay consistent with the rest of the guide.

Exercise 3 — Explain this lesson's boundary in your own words. In 2-3 sentences, using this lesson's blueprint analogy, explain why dbt debug can confirm that dbt_project.yml is valid without any real connection to a warehouse existing yet.

See solution

dbt_project.yml describes the project's shape — what folders exist, what it's called, what materialization to use by default — without ever needing to open a connection to any warehouse to validate itself; it's pure structure, like a blueprint that can be fully reviewed on a desk, without the lot it's going to be built on existing yet. profiles.yml, on the other hand, is what gives dbt the concrete credentials and path to connect to something real (in this case, a kiosko.duckdb file) — without it, dbt can perfectly well understand the project's shape, but has nowhere physical to send a CREATE VIEW or CREATE TABLE.

Summary and next step

In this lesson you built the first real piece of Kiosko's project: the kiosko_analytics/ folder, with dbt_project.yml written by hand and models/ created (still empty). You saw a dbt project's full anatomy — what's required, what's optional until you need it, and what dbt generates automatically — and confirmed with dbt debug that dbt already recognizes your project as valid, even with no connection yet. You also learned to tell apart three names that, in this project, share text on purpose but live in different places: the folder name, the project's name, and the profile that has to match profiles.yml exactly.

Before moving on you should be able to: name the three required pieces of a minimal dbt project; and explain why dbt debug can pass the dbt_project.yml check while still failing the profiles.yml one.

Lesson 6 closes that exact gap: you're going to write profiles.yml, connect Kiosko's project to a real kiosko.duckdb file, and see dbt debug pass both its checks for the first time.

Resources