PROFILES.YML
Located at ~/.dbt/profiles.yml — a project-local profiles.yml wins if present, DBT_PROFILES_DIR overrides the directory, and ~/.dvt/ is honored as a legacy fallback. This file defines all your database connections and selects the default target.
THE NO-YAML WAY — MANAGE CONNECTIONS FROM THE HUB
Since 0.2.27 you don't have to hand-edit this file at all. The hub's DVT Settings → Connectionstab is a full credential manager for your project's profile — arguably the most comfortable way to work with profiles.yml in either DVT or dbt:
- Run
dvt servein your project and open the hub (localhost:46100) → the ⚙ DVT SETTINGS card → CONNECTIONS. - Your project's profile is detected and shown at the top — the manager is scoped to it and never touches any other profile in the file.
- + ADD CONNECTIONopens a dropdown of every supported engine and bucket. Picking one renders that adapter's own pre-filled field set — hosts, ports and sensible defaults in place, secrets as write-only fields. The filesystem bucket gets a folder picker.
- CREATE lands the connection as an editable card in the list. Add the next one, and the next — duplicate names are rejected.
- Set the default target from the dropdown at the top, edit or delete any card in place (deleting the default is refused until another default is set).
- SYNC SOURCES runs
dvt syncin the background and reportsSYNCED— isolated adapter environments included.
Safety is built in: every write leaves a timestamped backup beside profiles.yml, output is structurally valid YAML for every profile in the file, secrets display masked and never leave your machine, and the write endpoints only answer on loopback — hosting the hub on your network never exposes credential writes. The reference below is still the source of truth when you prefer the editor.
CORE CONCEPT: DEFAULT TARGET
DVT works like dbt — your project has one primary default target of a specific adapter type. This is the engine where pushdown models execute their SQL. The target: key at the top level selects which output is the default.
You can define multiple outputs of the same type and host under the default target. These represent target environments — the same concept as dbt. For example, a dev and prod output both pointing to Snowflake on the same account.
All other outputs with different types or hosts act as external connections. DVT can extract data from these sources into the default target via the federation pipeline (Sling + DuckDB), or you can push models directly to them with config(target='...').
EXAMPLE
my_project:
target: pg_dev # ← active default target
outputs:
# ─── Target Environments (same engine, safe to switch) ───
pg_dev: # dev environment
type: postgres
host: db.internal.com
port: 5432
user: analyst
password: "{{ env_var('PG_PASSWORD') }}"
dbname: analytics_dev
schema: public
threads: 4
pg_prod: # prod environment (same engine)
type: postgres
host: db.internal.com
port: 5432
user: dvt_service
password: "{{ env_var('PG_PROD_PASSWORD') }}"
dbname: analytics_prod
schema: public
threads: 8
# ─── External Connections (different engines) ───
mysql_ops: # MySQL operational database
type: mysql
host: mysql.internal.com
port: 3306
user: readonly
password: "{{ env_var('MYSQL_PASSWORD') }}"
database: operations # required — MySQL schema = database
sf_warehouse: # Snowflake data warehouse
type: snowflake
account: xy12345.us-east-1
user: DVT_USER
password: "{{ env_var('SF_PASSWORD') }}"
database: PROD_DB
schema: RAW
warehouse: COMPUTE_WH
data_lake: # S3 bucket
type: s3
bucket: company-data-lake
region: us-east-1
access_key_id: "{{ env_var('AWS_ACCESS_KEY_ID') }}"
secret_access_key: "{{ env_var('AWS_SECRET_ACCESS_KEY') }}"
format: parquetSWITCHING THE DEFAULT TARGET
You can switch the default target with --target on the CLI or by changing the target: value in profiles.yml. But not all switches are equal:
Same adapter, same host
Harmless. Both outputs are the same engine on the same host. All models work unchanged. This is the standard dbt workflow for promoting across environments.
Same adapter, different host
Harmless. Same SQL dialect, different server. All pushdown models work unchanged. Use this for migrating between database instances.
Different adapter type
Breaking change. Pushdown models are written in the old engine's SQL dialect and will fail on the new engine. The entire project's pushdown models would need to be refactored for the new engine's syntax. Extraction models (DuckDB SQL) are unaffected.
# Safe: environment switch (same engine) dvt run --target pg_prod # Safe: target migration (same engine, different host) dvt run --target pg_staging # Dangerous: engine shift (different adapter type!) # Pushdown models written in PostgreSQL SQL will fail on Snowflake dvt run --target sf_warehouse
DVT does not block an engine shift — it transparently runs dbt from the new adapter's isolated environment. The failure surfaces where it belongs: pushdown models written in the old dialect error on the new engine. Extraction models (DuckDB SQL) are always safe across engine switches — only pushdown models are affected.
TARGET RESOLUTION
DVT resolves the target for each model in this priority order:
MODEL CONFIG EXTENSIONS
Standard dbt config plus DVT extensions:
-- Standard dbt config (works unchanged)
{{ config(
materialized='incremental',
unique_key='id',
schema='analytics',
tags=['finance']
) }}
-- DVT extension: target override
-- Materializes to a different output than the default
{{ config(
materialized='table',
target='sf_warehouse'
) }}
-- DVT extension: bucket target with format
{{ config(
materialized='table',
target='data_lake',
format='delta' -- parquet, delta, csv, json, jsonl, avro
) }}ENVIRONMENT VARIABLES
Use {{ env_var('VAR_NAME') }} in profiles.yml to reference environment variables. Never hardcode credentials.
| VARIABLE | PURPOSE |
|---|---|
| DBT_PROFILES_DIR | Override the profiles.yml directory (same variable dbt uses) |
| DVT_SLING_BINARY | Point at your own Sling binary instead of the bundled one |