Connect dbt Project
On this page, you will:
- Understand how Lightdash discovers dbt models and metrics
- Configure your dbt project for optimal Lightdash integration
- Learn the
metatag structure for defining metrics and dimensions - Set up Lightdash-specific YAML configuration
- Troubleshoot common discovery issues
Overview
Lightdash is dbt-native — it reads your dbt project directly from GitHub, parses YAML files, and discovers models, metrics, and dimensions. There's no separate UI configuration for metrics. Everything is defined in code, version-controlled, and testable.
This page covers how to structure your dbt project so Lightdash can discover and understand your models.
┌─────────────────────────────────────────────────────────────────────────┐
│ LIGHTDASH + DBT INTEGRATION │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ dbt-transform Repository │
│ ──────────────────────── │
│ │
│ models/ │
│ ├── marts/ │
│ │ └── core/ │
│ │ ├── fct_exchange_rates.sql ← SQL model │
│ │ └── fct_exchange_rates.yml ← Metrics, dimensions, meta │
│ │ │
│ │ Lightdash reads YAML files: │
│ │ • `meta:` tags → metrics, dimensions, hidden fields │
│ │ • `description:` → field documentation │
│ │ • `columns:` → discover dimensions │
│ │ │
│ └── Generates: │
│ • Lightdash Explore view │
│ • Drag-and-drop interface │
│ • Pre-defined metrics │
│ │
└─────────────────────────────────────────────────────────────────────────┘
How Lightdash Discovers Models
When you connect Lightdash to your dbt repository and compile the project, Lightdash:
- Clones the dbt repository (GitHub, GitLab, etc.)
- Runs
dbt depsto install packages frompackages.yml - Runs
dbt compileto generatemanifest.json(the dbt DAG) - Parses
manifest.jsonto discover: - All models in
models/directory - Column definitions from YAML files
metatags for metrics, dimensions, and configuration-
Relationships and joins between models
-
Creates Lightdash Explores — one Explore per dbt model
Each Explore provides a drag-and-drop interface with: - Dimensions (columns from the model) - Metrics (aggregations defined in YAML) - Filters (WHERE clauses) - Joins (relationships to other models)
dbt Project Structure for Lightdash
Lightdash works best with a well-structured dbt project:
dbt-transform/
├── models/
│ ├── marts/
│ │ ├── core/
│ │ │ ├── fct_exchange_rates.sql
│ │ │ ├── fct_exchange_rates.yml ← Metrics defined here
│ │ │ ├── dim_products.sql
│ │ │ └── dim_products.yml
│ │ └── crm/
│ │ ├── fct_contacts.sql
│ │ └── fct_contacts.yml
│ └── staging/
│ └── ... (not exposed to Lightdash — use REPORTING schema)
├── dbt_project.yml
└── packages.yml
Key principles:
- One YAML file per model (not combined)
- Use meta tags to define metrics and configure Lightdash behavior
- Only expose BI-ready models to Lightdash (via REPORTING schema or Lightdash config)
Configuring dbt_project.yml for Lightdash
Add Lightdash-specific configuration to dbt_project.yml:
# dbt_project.yml
name: dbt_transform
version: 1.0.0
config-version: 2
profile: dbt_transform
model-paths: ["models"]
analysis-paths: ["analyses"]
test-paths: ["tests"]
seed-paths: ["seeds"]
macro-paths: ["macros"]
target-path: "target"
clean-targets:
- "target"
- "dbt_packages"
models:
dbt_transform:
# Hide staging and intermediate models from Lightdash
staging:
+meta:
lightdash:
enabled: false # Don't create Explores for staging models
intermediate:
+meta:
lightdash:
enabled: false # Don't create Explores for intermediate models
# Expose only mart models to Lightdash
marts:
+meta:
lightdash:
enabled: true
+materialized: table
+schema: marts
This configuration: - Hides staging and intermediate models from Lightdash (they're not BI-ready) - Exposes only mart models (fact and dimension tables)
Alternative: Use REPORTING Schema
Instead of enabled: false, you can control exposure by only querying the REPORTING schema in Lightdash. Models not published to REPORTING won't be discoverable.
Defining Metrics in dbt YAML
Metrics are defined in the meta section of your model YAML files. Lightdash uses this to generate aggregation options in the UI.
Example: fct_exchange_rates.yml
# models/marts/core/fct_exchange_rates.yml
version: 2
models:
- name: fct_exchange_rates
description: Daily exchange rates for GBP to various currencies
meta:
lightdash:
label: "Exchange Rates" # Display name in Lightdash
group: "Finance" # Group in sidebar
columns:
- name: rate_date
description: Date of the exchange rate
meta:
dimension:
type: date # Lightdash will treat this as a date dimension
- name: base_currency
description: Base currency (always GBP)
meta:
dimension:
type: string
- name: target_currency
description: Target currency (USD, EUR, etc.)
meta:
dimension:
type: string
- name: exchange_rate
description: Exchange rate from base to target currency
meta:
dimension:
type: number
round: 4 # Round to 4 decimal places in Lightdash
metrics:
# Define metrics that aggregate this column
average_exchange_rate:
type: average
label: "Average Exchange Rate"
description: "Average exchange rate across selected date range"
min_exchange_rate:
type: min
label: "Minimum Exchange Rate"
max_exchange_rate:
type: max
label: "Maximum Exchange Rate"
- name: exchange_rate_id
description: Surrogate key for this fact
meta:
dimension:
hidden: true # Hide from Lightdash UI (not useful for analysis)
Metrics Types
Lightdash supports these metric types:
| Type | Description | Example |
|---|---|---|
count |
Count of rows | COUNT(*) |
count_distinct |
Distinct count | COUNT(DISTINCT target_currency) |
sum |
Sum of values | SUM(exchange_rate) |
average |
Average of values | AVG(exchange_rate) |
min |
Minimum value | MIN(exchange_rate) |
max |
Maximum value | MAX(exchange_rate) |
median |
Median value (if database supports) | MEDIAN(exchange_rate) |
percentile |
Percentile (requires percentile param) |
PERCENTILE_CONT(0.95) |
Dimension Types
Lightdash automatically infers dimension types from dbt column types, but you can override:
| Type | Used For | Features |
|---|---|---|
string |
Text fields | Filters: equals, contains, starts with |
number |
Numeric values | Filters: equals, greater than, less than, between |
date |
Date fields | Date pickers, date ranges, date grouping (day/week/month/year) |
timestamp |
Datetime fields | DateTime pickers, time-based grouping |
boolean |
True/false fields | Checkbox filters |
Example: dim_products.yml
# models/marts/core/dim_products.yml
version: 2
models:
- name: dim_products
description: Product dimension table
meta:
lightdash:
label: "Products"
group: "Catalogue"
columns:
- name: product_key
description: Surrogate key for the product
meta:
dimension:
hidden: true
- name: product_id
description: Natural key (product ID from source system)
meta:
dimension:
type: string
label: "Product ID"
- name: product_name
description: Product name
meta:
dimension:
type: string
- name: product_category
description: Product category
meta:
dimension:
type: string
- name: product_price
description: Current product price in GBP
meta:
dimension:
type: number
round: 2
format: currency
currency: GBP
metrics:
average_price:
type: average
label: "Average Product Price"
format: currency
currency: GBP
max_price:
type: max
label: "Most Expensive Product"
- name: is_active
description: Whether the product is currently active
meta:
dimension:
type: boolean
label: "Active Product"
- name: created_at
description: When the product was first added
meta:
dimension:
type: timestamp
Advanced: Custom SQL Metrics
You can define custom SQL metrics for more complex aggregations:
# models/marts/core/fct_exchange_rates.yml
version: 2
models:
- name: fct_exchange_rates
# ... columns ...
meta:
lightdash:
metrics:
# Custom SQL metric
gbp_usd_volatility:
type: number
label: "GBP/USD Volatility (30 days)"
description: "Standard deviation of GBP/USD exchange rate over last 30 days"
sql: |
STDDEV(
CASE
WHEN ${target_currency} = 'USD'
THEN ${exchange_rate}
END
)
format: number
round: 4
# Percentage of rates above 1.25
high_rate_percentage:
type: number
label: "% of Rates Above 1.25"
sql: |
(COUNT(CASE WHEN ${exchange_rate} > 1.25 THEN 1 END) * 100.0)
/ COUNT(*)
format: percent
round: 2
SQL Injection Risk
Custom SQL metrics use ${column_name} syntax for column references. Always reference existing columns, never accept user input directly in custom SQL.
Joins and Relationships
Define relationships between models to enable joins in Lightdash:
# models/marts/core/fct_exchange_rates.yml
version: 2
models:
- name: fct_exchange_rates
description: Exchange rates fact table
meta:
lightdash:
joins:
# Join to dim_products (hypothetical — exchange rates don't actually join to products)
- join: dim_products
sql_on: ${fct_exchange_rates.target_currency} = ${dim_products.currency_code}
type: left # left, inner, full
More commonly, you'll join facts to dimensions:
# models/marts/crm/fct_contacts.yml
version: 2
models:
- name: fct_contacts
description: HubSpot contacts fact table
meta:
lightdash:
joins:
- join: dim_companies
sql_on: ${fct_contacts.company_id} = ${dim_companies.company_id}
type: left
Hidden Fields and Models
Hide Specific Columns
columns:
- name: internal_notes
meta:
dimension:
hidden: true # Don't show in Lightdash Explore
Hide Entire Models
# In dbt_project.yml
models:
dbt_transform:
staging:
+meta:
lightdash:
enabled: false
Or in the model YAML:
# models/marts/core/internal_model.yml
version: 2
models:
- name: internal_model
meta:
lightdash:
enabled: false # This model won't appear in Lightdash
Lightdash-Specific Configuration Options
Full list of meta options:
meta:
lightdash:
# Model-level config
enabled: true | false # Show/hide this model
label: "Display Name" # Override model name
group: "Category" # Group in sidebar (e.g., "Finance", "Marketing")
description: "Override description"
# Join configuration
joins:
- join: other_model_name
sql_on: "${this_model.column} = ${other_model.column}"
type: left | inner | full
# Custom metrics (model-level)
metrics:
metric_name:
type: number | string | date
sql: "CUSTOM SQL EXPRESSION"
label: "Display Name"
description: "Metric description"
format: number | percent | currency | id
round: 2
currency: GBP
# Column-level config
columns:
- name: column_name
meta:
dimension:
type: string | number | date | timestamp | boolean
label: "Display Name"
description: "Override column description"
hidden: true | false
round: 2 # For numbers
format: currency | percent | number | id
currency: GBP # For currency format
time_intervals: [DAY, WEEK, MONTH, YEAR] # For date dimensions
# Column-level metrics
metrics:
metric_name:
type: count | count_distinct | sum | average | min | max | median
label: "Display Name"
description: "Metric description"
Compiling and Syncing
After updating your dbt YAML files:
Lightdash Cloud
- Commit and push changes to GitHub
- If auto-sync is enabled: Lightdash automatically recompiles
- If not: Navigate to Settings → Project → Compile project
Self-Hosted Lightdash
Same process as Cloud — Lightdash pulls from GitHub and recompiles.
Local Testing (Optional)
Test Lightdash locally before deploying:
# Clone the Lightdash CLI (optional)
npm install -g @lightdash/cli
# Login to your Lightdash instance
lightdash login https://your-lightdash-url
# Compile and preview changes
lightdash preview
This starts a local Lightdash instance with your changes for testing before pushing to GitHub.
Troubleshooting Discovery Issues
Model Not Appearing in Lightdash
Check:
1. Is the model materialised? (dbt run completed successfully)
2. Is the model in the correct schema? (Lightdash queries REPORTING if configured)
3. Is enabled: false set anywhere in the YAML or dbt_project.yml?
4. Did you recompile the project in Lightdash after adding the model?
Metrics Not Showing
Check:
1. Is the meta.metrics block correctly indented under the column?
2. Is the metric type valid? (sum, average, count, etc.)
3. Did you recompile after adding metrics?
Debug:
# Check the compiled manifest
cat target/manifest.json | jq '.nodes."model.dbt_transform.fct_exchange_rates".meta'
If meta is empty, the YAML is not being parsed correctly.
Joins Not Working
Check:
1. Are both models enabled in Lightdash?
2. Is the sql_on condition valid SQL?
3. Are you using ${model.column} syntax (not bare column names)?
Example: Full fct_exchange_rates.yml
version: 2
models:
- name: fct_exchange_rates
description: |
Daily exchange rates for GBP to various currencies.
Source: ECB and Yahoo Finance via dlt.
meta:
lightdash:
label: "Exchange Rates"
group: "Finance"
columns:
- name: exchange_rate_id
description: Surrogate key
tests:
- unique
- not_null
meta:
dimension:
hidden: true
- name: rate_date
description: Date of the exchange rate
tests:
- not_null
meta:
dimension:
type: date
label: "Date"
time_intervals: [DAY, WEEK, MONTH, QUARTER, YEAR]
- name: base_currency
description: Base currency (always GBP)
tests:
- not_null
- accepted_values:
values: ['GBP']
meta:
dimension:
type: string
- name: target_currency
description: Target currency
tests:
- not_null
meta:
dimension:
type: string
label: "Currency"
- name: exchange_rate
description: Exchange rate (1 GBP = X target currency)
tests:
- not_null
meta:
dimension:
type: number
round: 4
label: "Exchange Rate"
metrics:
average_rate:
type: average
label: "Average Exchange Rate"
round: 4
min_rate:
type: min
label: "Minimum Rate"
round: 4
max_rate:
type: max
label: "Maximum Rate"
round: 4
- name: loaded_at
description: Timestamp when this row was loaded
meta:
dimension:
type: timestamp
hidden: true
Summary
You've learned how to connect your dbt project to Lightdash:
- Lightdash reads dbt YAML — metrics, dimensions, and relationships defined in code
- Use
metatags to configure Lightdash behavior (labels, types, hidden fields) - Define metrics in
columns.meta.metrics(average, sum, count, custom SQL) - Configure dimensions with types, formats, and display options
- Hide staging/intermediate models to expose only BI-ready tables
- Joins defined in
meta.lightdash.joinsenable cross-model queries - Recompile after changes to sync Lightdash with your dbt project
Your dbt project is now the single source of truth for metrics and business logic. Changes in Git automatically flow to Lightdash.
What's Next
Now that Lightdash understands your dbt models, define metrics in your existing mart models to enable self-service analytics.
Continue to Define Metrics →