Skip to content

Sources

Sources define where a board reads data from. Most projects use one warehouse connection for SQL queries, but a board can also read files, HTTP endpoints, inline values, or dbt models.

Connection sources are defined once, in the project's root dbt_charts.yml, as a named sources: registry. A board (or charts/meta.yml) never defines a connection inline; it only references a source by name:

# dbt_charts.yml
sources:
  analytics:
    type: dbt_profile
    profile: my_dbt_project
    target: dev
source: analytics   # References the "analytics" source above

queries:
  tickets: SELECT * FROM dundersign.tickets

Every sources: block on this page belongs in dbt_charts.yml, not in a board. A board's own source: field always holds a name (or, for a single colocated data file, an inline path; see Inline File Sources), never a connection definition.

dbt Charts sources: are connection definitions. They are separate from dbt sources: resources, which document raw tables inside a dbt project. If you are setting up dbt source tables, use dbt's source properties reference.

Source Types

For warehouse-backed SQL, dbt Charts uses dbt-compatible connection settings. In most dbt projects, use type: dbt_profile and let dbt own the adapter-specific credentials. Direct warehouse source types use the same field names as dbt profiles, but they are flat because boards read data; they do not build dbt models.

You do not need profiles.yml for direct sources such as postgres, snowflake, bigquery, duckdb, or sqlite. dbt Charts uses the direct source block as the connection config. The dbt links below are still useful because they document the same adapter credential fields.

Source dbt Charts setup Credential fields / setup
Existing dbt project type: dbt_profile dbt profiles.yml
BigQuery type: bigquery BigQuery connection
DuckDB type: duckdb DuckDB connection
Postgres type: postgres Postgres connection
Redshift type: redshift Redshift connection
Snowflake type: snowflake Snowflake connection
MySQL / MariaDB type: mysql dbt community adapter; start with supported data platforms
SQL Server type: dbt_profile dbt-sqlserver connection (install dbt-charts[sqlserver])
Trino / Presto / Starburst type: trino Trino connection (install dbt-charts[trino])
Athena type: dbt_profile dbt-athena connection (install dbt-charts[athena])
ClickHouse type: dbt_profile dbt-clickhouse connection (install dbt-charts[clickhouse])
SQLite type: sqlite dbt Charts direct source; no dbt setup
Databricks type: dbt_profile Databricks connection
Spark type: dbt_profile Apache Spark connection
dbt models sql query using ref() with a dbt profile source dbt profiles.yml
CSV files type: csv Files and APIs
Parquet files type: parquet Files and APIs
JSON files type: json Files and APIs
HTTP endpoints type: http query Files and APIs
Inline static data type: values query No source setup

ClickHouse and max_execution_time. max_query_duration_seconds is a ceiling. If your dbt profile's custom_settings already sets a stricter max_execution_time, dbt Charts keeps yours; a looser one is capped at the ceiling. ClickHouse types the setting as whole seconds and reads 0 as no limit, so a fractional or zero value is rejected rather than silently disabling the cap.

ClickHouse read-only users need readonly = 2. dbt Charts caps every query with ClickHouse's max_execution_time, sent as a per-request setting. A readonly = 1 user cannot change any setting, so the connection fails at setup with ClickHouse's own Cannot modify 'max_execution_time' setting in readonly mode. readonly = 2 is the read-only mode ClickHouse gives BI clients: read-only queries, settings changes allowed.

SQL Server and query_timeout. max_query_duration_seconds is enforced through your dbt profile's query_timeout, which T-SQL needs because it has no per-statement SET. A stricter query_timeout of your own is kept; 0 means no cap and is replaced by the ceiling. The mssql-python backend ignores query_timeout, so dbt Charts refuses it rather than run uncapped; use the default pyodbc backend. SQL Server also needs Microsoft ODBC Driver 17 or 18 installed on the host, which pip cannot provide. A query that fails on SQL Server is not retried, including one that failed because an idle session was dropped; run it again.

dbt Profile Sources

dbt Charts can either read your warehouse credentials from a dbt profile or accept a direct source definition with dbt-style field names.

Use dbt_profile when your project already has profiles.yml:

# dbt_charts.yml
sources:
  analytics:
    type: dbt_profile
    profile: my_dbt_project
    target: dev

dbt Charts resolves profiles.yml in this order:

  1. profiles_dir on the source config
  2. DBT_PROFILES_DIR
  3. The linked dbt project directory, next to dbt_charts.yml by default, or an external directory named via --dbt-project-dir / DBT_PROJECT_DIR / dbt_project_dir: (see --dbt-project-dir)
  4. ~/.dbt/profiles.yml

If none of those locations contains profiles.yml, dbt Charts raises an error.

Which manifest ref() resolves against

target: picks the connection, but ref() and source() resolve against a dbt manifest, and dbt does not record which target built it. With target: prod and a manifest from dbt parse --target dev, queries run against dev relations. Point the source at the manifest built for its target with target_path:, the same meaning as dbt's --target-path, relative to the dbt project directory:

# dbt_charts.yml
sources:
  analytics:
    type: dbt_profile
    profile: my_dbt_project
    target: prod
    target_path: target/prod
dbt parse --target prod --target-path target/prod

Without target_path:, the manifest is <dir>/manifest.json, where <dir> is DBT_TARGET_PATH, then target-path: in dbt_project.yml, then target. The directory must be relative to the dbt project (ERR-DBT-TARGET-PATH-INVALID otherwise). A missing manifest at the resolved path raises ERR-DBT-MANIFEST-MISSING naming it; dbt Charts never falls back to another one. A query inlined through {{ queries.X }} resolves its ref() calls against the including query's source.

Who validates the target

profiles.yml is dbt's file, so dbt validates it; dbt Charts applies no schema of its own. Every field your installed dbt adapter accepts works, including threads and dbt's canonical database: / schema: spelling for BigQuery (project: / dataset: work too, exactly as in dbt). A field dbt requires but your target omits is reported when the dashboard runs, naming the profile and target.

The flip side: a misspelled key is not an error. dbt ignores keys it does not recognize, so hostt: localhost is silently unused, by dbt Charts and by dbt run alike. If a connection behaves unexpectedly, check the spelling of your target's fields.

This differs from the direct warehouse sources below, where dbt Charts owns the schema and an unknown key is an error.

Installing that adapter is a separate step: dbt Charts connects through a Python adapter package, which ships as an extra. See Warehouse adapters.

Direct Warehouse Sources

Direct warehouse sources are useful for small projects that do not share a dbt project. This is the full connection config; no profiles.yml file is required. For larger dbt projects, prefer type: dbt_profile.

Postgres

# dbt_charts.yml
sources:
  analytics:
    type: postgres
    host: "{{ env_var('ANALYTICS_HOST') }}"
    port: 5432
    dbname: analytics
    schema: public
    user: "{{ env_var('ANALYTICS_USER') }}"
    password: "{{ env_var('ANALYTICS_PASSWORD') }}"

DuckDB

# dbt_charts.yml
sources:
  local_db:
    type: duckdb
    path: ./data/analytics.duckdb
    schema: crm

DuckDB sources are local-only. A .duckdb file is a version-coupled, single-writer binary, so dbt Charts Cloud does not read .duckdb files from connected repositories. For Cloud, export the tables to Parquet or CSV and register them as file sources instead.

SQLite

# dbt_charts.yml
sources:
  local_sqlite:
    type: sqlite
    path: ./data/analytics.sqlite

Files and APIs

CSV, Parquet, JSON, and HTTP sources do not need dbt. They are simpler local dbt Charts source definitions for examples, prototypes, and lightweight external data.

CSV

A CSV source is a namespace of relations. Each key under files: is a table name; the value is the path to the CSV file relative to the project root.

# dbt_charts.yml
sources:
  sales_csv:
    type: csv
    files:
      sales: assets/data/sales.csv

Then query it by name:

queries:
  sales:
    source: sales_csv
    sql: SELECT * FROM sales

A single source can expose multiple CSV files as separate tables:

# dbt_charts.yml
sources:
  sales_data:
    type: csv
    files:
      orders: assets/data/orders.csv
      returns: assets/data/returns.csv

JSON

# dbt_charts.yml
sources:
  product_json:
    type: json
    files:
      products: assets/data/products.json
queries:
  products:
    source: product_json
    sql: SELECT * FROM products

Parquet

# dbt_charts.yml
sources:
  events:
    type: parquet
    files:
      events: assets/data/events.parquet
queries:
  event_counts:
    source: events
    sql: SELECT * FROM events

Guardrails

Two limits bound a single file source's growth: execution.file_source_max_tables caps the number of table entries a single files: map may declare (default 500), and execution.file_source_max_bytes caps each table's uncompressed byte size (default 5 GB), summed across all files matched by its path or glob. Every format is measured the same way: the file's own bytes for CSV and JSON, and for Parquet the uncompressed total recorded in the file's footer, so choosing the compact format never costs you budget. Both are hard errors, not truncation. Narrow the files: map, raise the limit in dbt_charts.yml, or use a database connection for data at that scale. A hosted deployment may pin its own ceiling, and a project's setting can only lower it, never raise it; where a ceiling is what fired, the limit below is not the knob that moves:

# dbt_charts.yml
execution:
  file_source_max_tables: 100
  file_source_max_bytes: 1000000000  # 1 GB

Exploring a file source

dct query runs ad-hoc SQL against a file source directly, the same way it does against a warehouse source:

dct query sales_csv 'SELECT * FROM sales LIMIT 5'

--describe (column schema without running the query) also works against a file source:

dct query sales_csv 'SELECT * FROM sales' --describe

Unlike a warehouse DESCRIBE, this is not free: DuckDB has to materialize the source's files (read and parse them) before it can describe anything. Still far cheaper than running the query in full, since it returns column metadata rather than the result set, and the materialized tables land in the cache. That cache is in-memory and dies with the process, though, so a following dct query invocation — a fresh process — gets no benefit from it and re-reads the files from scratch. The warmth only pays off inside one long-lived process that runs multiple queries against the source, such as dct serve, a single render, or one dct validate --warehouse sweep.

HTTP

Unlike SQL and file queries, an HTTP query is self-contained: set url: (and optional headers:, method:, body:) directly on the query. It has no source: field; there is no named registry entry to reference.

Runs under local dct only; Not supported on dbtcharts.com.

queries:
  forecast:
    type: http
    url: "https://api.example.com/forecast"

See Queries for the full HTTP query field reference.

Inline File Sources

A single CSV, JSON, or Parquet file that only one query reads doesn't need a named registry entry; reference the file directly on source:, as a path containing / or ending in a data-file extension:

queries:
  sales:
    source: ./data/sales.csv
    sql: SELECT * FROM sales

The path resolves against the board file's own directory or the project root, whichever exists (both existing at once is a compile error naming both). The table name is the file's stem (sales.csv → table sales); a stem that isn't a valid SQL identifier is a compile error rather than a silently sanitized name; rename the file or use the named-registry form below. Only a single file is allowed inline (no files: list); an inline file source is always materialized straight from the file's git content.

Use the named-registry form (type: csv/json/parquet with a files: map in dbt_charts.yml, above) instead when a file is shared across boards or a source exposes more than one file as separate tables.

Source Defaults

Set source: on a board to make all queries in that board use a default source by name. A charts/meta.yml (or a directory's own meta.yml) can set the same field to default every board beneath it:

source: finance

queries:
  monthly_revenue:
    sql: SELECT month, revenue FROM finance_revenue

  sales_pipeline:
    source: sales
    sql: SELECT stage, amount FROM sales_pipeline

Sources are resolved from most specific to broadest:

  1. Query-level source:
  2. Board-level source:
  3. The nearest ancestor source: in the charts/meta.yml cascade

An unknown source name is a validation error.

Attribution

Every query dbt Charts sends carries attribution so your warehouse's cost tracking can tell a dbt Charts query apart from ad-hoc human querying. On BigQuery it arrives as job labels, queryable as a real column in INFORMATION_SCHEMA.JOBS; on other warehouses it arrives as a leading JSON comment on the query text.

dbt Charts sets the engine half itself; you don't configure it:

Label Meaning
app Always dbt-charts. The one key to filter on; dbt emits app=dbt under the same key, so one predicate covers both.
dbt_charts_version The dct version that sent the query.
dbt_charts_surface Which entry point: cli, serve, or cloud.
dbt_charts_board The dashboard the query belongs to.
dbt_charts_query The query's name within that dashboard.
dbt_charts_target The dbt target, when the query names one.

A host that knows who is asking adds three more, opaque per-request labels; no emails or display names, since warehouse query history is broadly readable and long-retained:

Label Meaning
dbt_charts_actor Opaque ID of the person or agent who caused the query.
dbt_charts_client Opaque ID of the software that asked: a browser session, an AI copilot turn, a connected MCP client.
dbt_charts_request A fresh opaque ID per call, correlating this query to the host's own audit log.

Only Cloud sets these today; it is the only host with a signed-in principal. The CLI and dct serve have no notion of "who," so they omit all three rather than inventing a value.

Like dbt_charts_board/_query/_target, only BigQuery gets dbt_charts_actor, _client, and _request as real key/value labels; on every other warehouse they ride the query comment along with the rest of the per-request payload (see below), so finding them means matching the comment text, not filtering a column.

Where it lands, per warehouse

Only BigQuery stores the whole payload as structured key/value. Elsewhere dbt Charts fills the warehouse's own identity field so app is a column you can filter, and the rest of the payload rides the query comment:

Warehouse Native field Carries
BigQuery job labels (JOBS.labels) everything, as key/value
Snowflake QUERY_TAG app, version, surface, as JSON
Databricks query tags app, version, surface, as JSON
Trino client_tags app, version, surface
Postgres application_name dbt-charts
Redshift, Athena, ClickHouse, DuckDB, SQLite none query comment only

Setting the native field costs nothing: it travels with the connection, not with each query. If you have already set query_tag, application_name, or client_tags yourself, dbt Charts leaves your value alone.

Redshift and Athena are deliberately left out: Redshift's query_group routes WLM queues and Athena's work_group routes data limits and engine settings, so writing either could move your queries somewhere else, too high a price for a label. ClickHouse's log_comment is inert but is a setting, which a readonly = 1 user cannot send at all, and the query comment already lands in system.query_log.

Your own labels

Add ownership labels with attribution: on a source. They apply to every query against it:

# dbt_charts.yml
sources:
  warehouse:
    type: bigquery
    project: my-gcp-project
    dataset: analytics
    attribution:
      team: architecture-analytics
      cost_center: data-platform

Keys and values must match BigQuery's label rules: keys [a-z][a-z0-9_-]{0,62}, values [a-z0-9_-]{0,63}, on every warehouse, so the same source config behaves identically everywhere. A value that doesn't match is an error, not a silently rewritten label. The dbt_charts_ prefix and the app key are reserved for the engine.

env_var() works here like anywhere else in a source:

# dbt_charts.yml
sources:
  warehouse:
    type: bigquery
    project: my-gcp-project
    dataset: analytics
    attribution:
      env: "{{ env_var('DCT_ENV', 'dev') }}"

To find your dbt Charts spend on BigQuery:

SELECT creation_time, total_bytes_billed, user_email
FROM `region-us`.INFORMATION_SCHEMA.JOBS
WHERE creation_date = CURRENT_DATE()
  AND EXISTS (SELECT 1 FROM UNNEST(labels) l WHERE l.key = 'app' AND l.value = 'dbt-charts')

This works whether the query ran under a service account or an individual developer's credentials.

Environment Variables

Use env_var() in source configuration to keep credentials out of YAML files:

# dbt_charts.yml
sources:
  analytics:
    type: postgres
    host: "{{ env_var('DB_HOST', 'localhost') }}"
    user: "{{ env_var('DB_USER') }}"
    password: "{{ env_var('DB_PASSWORD') }}"

Store credentials in .env or your deployment environment:

DB_HOST=localhost
DB_USER=analytics_user
DB_PASSWORD=secret123

Keep credentials out of boards

env_var() is for source configuration. Do not put secrets in board YAML, query text, or rendered output.

Read-only Posture

dbt Charts boards read data. They never intentionally write to your warehouse, but connection-level enforcement depends on the source type.

DuckDB and SQLite are opened with driver-level read-only settings. Every other warehouse should use credentials that can only read data. dbt Charts also applies an in-process SQL allowlist before execution, but SELECT-only credentials are the warehouse-side defense.

Warehouse In-process enforcement Recommended operator posture
DuckDB Driver read-only mode Handled by dbt Charts
SQLite Driver read-only URI mode Handled by dbt Charts
Postgres SQL allowlist only SELECT-only role
MySQL / MariaDB SQL allowlist only SELECT-only user
Snowflake SQL allowlist only SELECT-only role
BigQuery SQL allowlist only roles/bigquery.dataViewer or equivalent
Redshift SQL allowlist only SELECT-only IAM role or database user
dbt profile sources Depends on adapter SELECT-only profile credential

File access from SQL

dct serve runs strictly read-only: author SQL cannot reach the filesystem or network through DuckDB, so functions like read_csv(...), read_json_auto(...), read_parquet(...), and httpfs are not available in query SQL. Point data at a file source instead; declare it with type: csv, type: json, or type: parquet and a files: mapping (see Files and APIs), then query the table by name. dbt Charts parses the file and loads it for querying without granting SQL any file access.

  • Queries for the SQL, values, HTTP, and schema query types (dbt models are queried as plain SQL via ref(), not a separate query type)
  • Quick Guide for the shortest syntax overview