Learning Platform
Глоссарий Troubleshooting
Урок 04.04 · 18 мин
Начальный
profiles.ymlduckdbenv_vartargets

profiles.yml для DuckDB

profiles.yml — это второй ключевой файл dbt после dbt_project.yml. Один говорит «что моделировать», другой — «куда подключаться». Этот урок разбирает структуру profiles.yml для DuckDB и паттерны для dev/prod environments.


Где живёт profiles.yml

По умолчанию dbt ищет profiles.yml в ~/.dbt/profiles.yml:

  • macOS / Linux: /Users/<you>/.dbt/profiles.yml или /home/<you>/.dbt/profiles.yml
  • Windows: C:\Users\<you>\.dbt\profiles.yml

Это намеренно вне проекта, по двум причинам:

  1. Безопасность: profiles.yml содержит credentials. Внутри проекта легко случайно закоммитить.
  2. Переиспользование: один пользователь работает с несколькими проектами; profiles.yml содержит profile для каждого.

Альтернатива — переопределить через переменную окружения или флаг:

export DBT_PROFILES_DIR=/path/to/profiles
# или
dbt run --profiles-dir /path/to/profiles

Это часто используется в CI: pipeline кладёт profiles.yml в /tmp/dbt/, передаёт через флаг — credentials живут только в pipeline secrets.


Минимальный profiles.yml для DuckDB

После dbt init jaffle_shop файл выглядит так:

jaffle_shop:
  outputs:
    dev:
      type: duckdb
      path: ./dev.duckdb
      schema: main
      threads: 4
  target: dev

Что здесь:

ПолеЧто означает
jaffle_shop:Имя profile. Совпадает с profile: в dbt_project.yml
target: devTarget по умолчанию (если не указали dbt run --target prod)
outputs:Список доступных targets (sub-сред)
outputs.dev.type: duckdbКакой adapter использовать
outputs.dev.path:Путь к .duckdb файлу
outputs.dev.schema: mainDefault схема для создания таблиц
outputs.dev.threads: 4Сколько моделей собирать параллельно

При dbt run без флагов dbt использует target=dev. Чтобы переключить — dbt run --target prod (если в outputs есть prod).


Анатомия каждого поля

type: duckdb

Указывает adapter. Должен совпадать с установленным пакетом — у нас dbt-duckdb, поэтому type: duckdb.

Другие варианты для других warehouses:

  • type: snowflake (требует pip install dbt-snowflake)
  • type: bigquery
  • type: postgres
  • type: redshift
  • type: databricks

Один проект — один тип warehouse в одном target. Через разные targets можно подключать разные базы (например, dev = DuckDB, prod = Snowflake), но это редкий кейс.

path: ./dev.duckdb

Путь к файлу базы DuckDB. Относительный путь резолвится относительно текущей директории при запуске dbt, не относительно проекта.

WARNING

Самая частая ошибка джунов: path: ./dev.duckdb в profiles.yml, запуск dbt run из подпапки models/ — файл создаётся в models/dev.duckdb, а не в корне проекта. Решение: используйте абсолютный путь или паттерн {{ env_var('DBT_DUCKDB_PATH', '/full/path/dev.duckdb') }}.

Альтернативы:

# Абсолютный путь — надёжно
path: /Users/lev/projects/jaffle_shop/dev.duckdb

# Через env var
path: '{{ env_var("DBT_DUCKDB_PATH", "./dev.duckdb") }}'

# In-memory (только для CI / тестов)
path: ':memory:'

:memory: — особый случай: база живёт только в памяти процесса, после dbt run исчезает. Полезно в CI: каждый запуск с чистого листа. Но не используйте :memory: с external models или incremental — будут гонки.

schema: main

DuckDB-специфика: schema — это namespace внутри базы. По умолчанию DuckDB создаёт схему main.

dbt создаёт модели в <database>.<schema>.<model_name>. Для DuckDB:

  • databasebasename(path).rsplit('.duckdb', 1)[0] = dev (от dev.duckdb)
  • schema — то, что в profiles.yml = main
  • model_name — имя файла модели

Так что модель orders.sql после dbt run будет доступна как dev.main.orders.

Можно делать кастомную схему per-model:

# В schema.yml или {{ config() }}
{{ config(schema='marts') }}

И dbt создаст модель в dev.main_marts.orders. По умолчанию dbt добавляет префикс к схеме (main_marts вместо marts) — это generate_schema_name behavior, см. модуль 12.

threads: 4

Сколько моделей dbt запускает параллельно. Для DuckDB:

  • 1 — последовательно, для отладки
  • 4 — нормальный default для локальной разработки
  • 8-16 — для больших проектов на multi-core машинах

DuckDB особенность: один процесс — один writer на файл. Если threads > 1, dbt все равно сериализует запись (но параллельно компилирует и подготавливает запросы). Так что увеличение threads даёт меньший выигрыш чем на Snowflake, где компьют разделён.


Multiple targets: dev / prod

В реальном проекте обычно несколько targets:

jaffle_shop:
  outputs:

    dev:
      type: duckdb
      path: './dev.duckdb'
      schema: main
      threads: 4

    prod:
      type: duckdb
      path: '/data/prod/jaffle_shop.duckdb'
      schema: main
      threads: 8

    ci:
      type: duckdb
      path: ':memory:'
      schema: main
      threads: 4

  target: dev   # default

Запуск:

# default = dev
dbt run

# Явно prod
dbt run --target prod

# CI с in-memory базой
dbt run --target ci

Что разнится между targets:

  • Путь к базе (dev.duckdb vs /data/prod/jaffle_shop.duckdb)
  • threads
  • Возможно schema (если хотите изолировать env’ы по схемам в одной базе)

В моделях через {{ target.name }} можно делать env-specific логику:

select *
from {{ ref('orders') }}
{% if target.name == 'dev' %}
where order_date >= current_date - interval 7 day
{% endif %}

Это значит «в dev запросе только последние 7 дней, в prod — все данные». Стандартный паттерн для ускорения dev итераций. См. модуль 12.


env_var: секреты не в файле

Захардкоженные пути и креды плохи. Лучше:

jaffle_shop:
  outputs:
    dev:
      type: duckdb
      path: "{{ env_var('DBT_DUCKDB_PATH') }}"
      schema: "{{ env_var('DBT_DUCKDB_SCHEMA', 'main') }}"
      threads: "{{ env_var('DBT_THREADS', '4') | int }}"
  target: dev

Что здесь:

  • {{ env_var('DBT_DUCKDB_PATH') }} — обязательная переменная, без неё dbt debug упадёт
  • {{ env_var('DBT_DUCKDB_SCHEMA', 'main') }} — переменная с default’ом main
  • {{ env_var('DBT_THREADS', '4') | int }} — конвертация в int

Запускать так:

export DBT_DUCKDB_PATH=/Users/lev/projects/jaffle_shop/dev.duckdb
dbt debug
dbt run

В CI переменные подаются через secrets:

# .github/workflows/ci.yml (фрагмент)
env:
  DBT_DUCKDB_PATH: /tmp/ci.duckdb
  DBT_THREADS: '8'
TIP

Best practice 2026: даже на DuckDB-проекте используйте env_var с самого начала. Это маленькая инвестиция (1 строка), которая окупится, когда перейдёте на production-warehouse. Pattern такой же будет для Snowflake/BigQuery, где env_var — единственный sane способ передавать креды.


DuckDB-specific опции

dbt-duckdb поддерживает дополнительные опции, которых нет в других adapters:

jaffle_shop:
  outputs:
    dev:
      type: duckdb
      path: './dev.duckdb'
      schema: main
      threads: 4
      extensions:
        - httpfs       # для чтения файлов с S3/HTTP
        - parquet      # для read_parquet функции
        - json         # для JSON manipulation
      settings:
        memory_limit: '4GB'
        temp_directory: '/tmp/duckdb_spill'
        max_temp_directory_size: '20GB'
  target: dev

Разберём:

extensions:

Список DuckDB extensions, которые автоматически загружаются. Полезные:

ExtensionЗачем
httpfsЧтение s3://, https:// URL прямо в SQL
parquetread_parquet('path')
jsonJSON-функции
postgresATTACH Postgres базы как readable source
sqliteATTACH SQLite
spatialGEO-функции
icebergIceberg-таблицы из S3

При первом запуске dbt сам скачивает extensions из DuckDB Extension Repository.

settings:

Опции DuckDB engine:

  • memory_limit: '8GB' — лимит RAM (по умолчанию 80% от физического)
  • temp_directory: '/tmp/duckdb_spill' — куда spill-ить, если не хватает RAM
  • max_temp_directory_size: '20GB' — лимит на spill
  • threads: 8 — количество DuckDB-уровень threads (отличается от dbt threads, см. ниже)
WARNING

threads в settings.threads (DuckDB-уровень) != threads на уровне profile (dbt-уровень). dbt-threads — сколько моделей параллельно компилируются/отправляются. DuckDB-threads — сколько ядер использует DuckDB внутри одного запроса. Обычно оставляйте DuckDB-threads на default (auto).

Прочие DuckDB опции:

  • attach: — подключение других баз (postgres://..., sqlite://...)
  • secrets: — credentials для S3/HTTP (часть DuckDB 1.0+ secrets API)
  • disable_transactions: true — отключить транзакции (для performance)
  • attach_temp_only: false — настройка для temp-схемы

Большинство из них — это middle/senior уровень. Подробнее в курсах dbt II / dbt III.


dbt debug: проверка profiles.yml

После любых изменений в profiles.yml:

dbt debug

Что проверяет:

  1. profiles.yml file — синтаксис YAML
  2. dbt_project.yml file — синтаксис YAML
  3. profile match — что profile: в project совпадает с ключом в profiles
  4. target match — что target: существует в outputs
  5. Connection test — что dbt может подключиться к указанной базе

Полный успешный output:

06:30:11  All checks passed!

Типичные failures:

1. Profile not found:

Could not load profile named 'jaffle_shop'
Required profile name 'jaffle_shop' was not found in /Users/lev/.dbt/profiles.yml

-> Имя profile в project.yml не совпадает с ключом в profiles.yml.

2. Target not found:

The profile 'jaffle_shop' does not have a target named 'prod'.
Defined targets: dev

-> Запустили dbt run --target prod, но в profiles.yml нет outputs.prod.

3. Connection test failed:

06:30:11  Connection test: [ERROR]
06:30:11    duckdb.duckdb.IOException: IO Error: Cannot open file "/data/prod.duckdb": No such file or directory

-> Путь к файлу не существует или нет permissions. Для DuckDB это часто относительный путь, который резолвится не оттуда.


Несколько profiles в одном файле

Один profiles.yml может содержать profiles для нескольких проектов:

jaffle_shop:
  outputs:
    dev:
      type: duckdb
      path: ./jaffle_shop_dev.duckdb
      schema: main
      threads: 4
  target: dev

analytics:
  outputs:
    dev:
      type: duckdb
      path: ./analytics_dev.duckdb
      schema: main
      threads: 4
    prod:
      type: snowflake
      account: ab12345.us-east-1
      user: dbt_runner
      password: '{{ env_var("SNOWFLAKE_PASSWORD") }}'
      role: TRANSFORMER
      database: ANALYTICS_PROD
      warehouse: TRANSFORM_WH
      schema: PUBLIC
      threads: 8
  target: dev

Какой profile использовать — определяется profile: в dbt_project.yml.


profiles.yml для CI

В CI обычно:

  1. Файл генерируется на лету из template + secrets
  2. Кладётся в /tmp/dbt/profiles.yml
  3. dbt запускается с --profiles-dir /tmp/dbt

Пример GitHub Actions фрагмента:

- name: Setup dbt profile
  run: |
    mkdir -p /tmp/dbt
    cat > /tmp/dbt/profiles.yml <<EOF
    jaffle_shop:
      outputs:
        ci:
          type: duckdb
          path: ':memory:'
          schema: main
          threads: 4
      target: ci
    EOF

- name: Run dbt
  run: dbt build --profiles-dir /tmp/dbt

Это снимает зависимость от ~/.dbt/profiles.yml в CI runner.


Связь profiles.yml и dbt_project.yml

Финальная картинка:

Связь profiles.yml и dbt_project.yml
dbt_project.ymlВ проекте. Поле profile: указывает на имя в profiles.yml
lookup by name
profiles.ymlВ ~/.dbt/profiles.yml. Содержит outputs.dev/prod/ci с connection settings
select by target
active targetВыбранный target определяет connection (type, path, schema, threads). По умолчанию target: dev, или --target prod через CLI
warehouseDuckDB файл, или Snowflake account, или BigQuery dataset

Этот lookup происходит на каждом dbt вызове.


Попробуй сам

  1. Откройте ~/.dbt/profiles.yml. Если у вас уже есть запись jaffle_shop от dbt init — посмотрите её.
  2. Добавьте второй target prod с другим путём:
    jaffle_shop:
      outputs:
        dev:
          type: duckdb
          path: ./dev.duckdb
          schema: main
          threads: 4
        prod:
          type: duckdb
          path: ./prod.duckdb
          schema: main
          threads: 8
      target: dev
  3. Запустите dbt debug — пройдёт.
  4. Запустите dbt run --target prod — будет создан второй файл prod.duckdb.
  5. Запустите ls *.duckdb — два файла.
  6. Перепишите path через env_var:
    path: "`{{ env_var('DBT_DUCKDB_PATH', './dev.duckdb') }}`"
  7. Запустите DBT_DUCKDB_PATH=./test.duckdb dbt run — создастся третий файл test.duckdb.

После этого у вас понимание, как переключать environments и через CLI, и через env vars.


DuckDB: in-process OLAP
Проверка знанийKnowledge check
Что произойдёт, если в profiles.yml поле path задано как './dev.duckdb' (относительный путь), и вы запустите 'dbt run' из подпапки 'models/'?
ОтветAnswer
Относительный путь в profiles.yml резолвится относительно ТЕКУЩЕЙ ДИРЕКТОРИИ при запуске dbt, а не относительно корня проекта. Поэтому если запустить 'dbt run' из подпапки 'models/', dbt создаст файл './dev.duckdb' внутри 'models/', а не в корне проекта. Это приводит к запутанным сценариям: один разработчик из корня создаёт файл в одном месте, другой из подпапки — в другом, тесты в IDE могут падать из-за того, что они запускаются из workspace root, а вы привыкли запускать из подпапки. Решения три: (1) использовать абсолютный путь, (2) запускать dbt всегда из корня проекта, (3) использовать env_var: path: "{{ env_var('DBT_DUCKDB_PATH', '/absolute/path/dev.duckdb') }}". В production-проектах принято (3) — это снимает problem и работает одинаково в dev/CI/prod.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 6. Почему profiles.yml хранится в ~/.dbt/ (вне папки проекта), а не внутри проекта вместе с dbt_project.yml?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 4