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
Это намеренно вне проекта, по двум причинам:
- Безопасность: profiles.yml содержит credentials. Внутри проекта легко случайно закоммитить.
- Переиспользование: один пользователь работает с несколькими проектами; 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: dev | Target по умолчанию (если не указали dbt run --target prod) |
outputs: | Список доступных targets (sub-сред) |
outputs.dev.type: duckdb | Какой adapter использовать |
outputs.dev.path: | Путь к .duckdb файлу |
outputs.dev.schema: main | Default схема для создания таблиц |
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: bigquerytype: postgrestype: redshifttype: databricks
Один проект — один тип warehouse в одном target. Через разные targets можно подключать разные базы (например, dev = DuckDB, prod = Snowflake), но это редкий кейс.
path: ./dev.duckdb
Путь к файлу базы DuckDB. Относительный путь резолвится относительно текущей директории при запуске dbt, не относительно проекта.
Самая частая ошибка джунов: 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:
- database —
basename(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.duckdbvs/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'
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 |
parquet | read_parquet('path') |
json | JSON-функции |
postgres | ATTACH Postgres базы как readable source |
sqlite | ATTACH SQLite |
spatial | GEO-функции |
iceberg | Iceberg-таблицы из S3 |
При первом запуске dbt сам скачивает extensions из DuckDB Extension Repository.
settings:
Опции DuckDB engine:
memory_limit: '8GB'— лимит RAM (по умолчанию 80% от физического)temp_directory: '/tmp/duckdb_spill'— куда spill-ить, если не хватает RAMmax_temp_directory_size: '20GB'— лимит на spillthreads: 8— количество DuckDB-уровень threads (отличается от dbt threads, см. ниже)
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
Что проверяет:
- profiles.yml file — синтаксис YAML
- dbt_project.yml file — синтаксис YAML
- profile match — что
profile:в project совпадает с ключом в profiles - target match — что
target:существует в outputs - 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 обычно:
- Файл генерируется на лету из template + secrets
- Кладётся в
/tmp/dbt/profiles.yml - 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
Финальная картинка:
Этот lookup происходит на каждом dbt вызове.
Попробуй сам
- Откройте
~/.dbt/profiles.yml. Если у вас уже есть записьjaffle_shopотdbt init— посмотрите её. - Добавьте второй 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 - Запустите
dbt debug— пройдёт. - Запустите
dbt run --target prod— будет создан второй файлprod.duckdb. - Запустите
ls *.duckdb— два файла. - Перепишите path через env_var:
path: "`{{ env_var('DBT_DUCKDB_PATH', './dev.duckdb') }}`" - Запустите
DBT_DUCKDB_PATH=./test.duckdb dbt run— создастся третий файлtest.duckdb.
После этого у вас понимание, как переключать environments и через CLI, и через env vars.
DuckDB: in-process OLAP