Skip to main content
The dbt-materialize adapter allows you to use dbt (data build tool) to build transformation pipelines in Materialize.
What is dbt? dbt is a transformation workflow tool that lets teams deploy analytics code following software engineering best practices like modularity, version control, testing, and CI/CD.

Requirements

  • Materialize v0.68.0 or later
  • Python 3.8 or later
  • dbt Core 1.0.0 or later

Installation

Install dbt-materialize via pip, optionally using a virtual environment:
# Create and activate a virtual environment
python3 -m venv dbt-venv
source dbt-venv/bin/activate

# Install dbt-core and the Materialize adapter
pip install dbt-core dbt-materialize

Verify Installation

dbt --version
You should see output indicating that the materialize adapter is installed.

Configuration

Profile Setup

Configure your dbt profile in ~/.dbt/profiles.yml:
materialize_project:
  target: dev
  outputs:
    dev:
      type: materialize
      host: instance.region.aws.materialize.cloud
      port: 6875
      user: [email protected]
      pass: mzp_your_app_password_here
      dbname: materialize
      schema: dbt_user
      cluster: quickstart
      sslmode: require
      threads: 1
      keepalives_idle: 0
      retries: 1

Configuration Parameters

ParameterDescriptionDefaultRequired
typeMust be materialize-Yes
hostMaterialize hostname-Yes
portConnection port6875Yes
userYour Materialize user email-Yes
passApp-specific password (starts with mzp_)-Yes
dbnameDatabase namematerializeYes
schemaDefault schema for dbt models-Yes
clusterCluster to use for materializationsquickstartNo
sslmodeSSL moderequireYes
threadsNumber of concurrent threads1No
keepalives_idleKeepalive idle time0No
retriesNumber of connection retries1No
search_pathOverride default search path-No
Always use sslmode: require when connecting to Materialize Cloud.

Production Profile Example

materialize_project:
  target: dev
  outputs:
    dev:
      type: materialize
      host: dev-instance.region.aws.materialize.cloud
      port: 6875
      user: [email protected]
      pass: "{{ env_var('DBT_MATERIALIZE_PASSWORD') }}"
      dbname: materialize
      schema: dbt_dev
      cluster: dev_cluster
      sslmode: require
      
    prod:
      type: materialize
      host: prod-instance.region.aws.materialize.cloud
      port: 6875
      user: [email protected]
      pass: "{{ env_var('DBT_MATERIALIZE_PASSWORD') }}"
      dbname: materialize
      schema: public
      cluster: prod_cluster
      sslmode: require

Supported Materializations

The primary materialization for Materialize. Creates a materialized view that incrementally maintains results.
-- models/active_users.sql
{{ config(materialized='materialized_view') }}

SELECT 
    user_id,
    COUNT(*) as event_count,
    MAX(event_timestamp) as last_seen
FROM {{ source('app', 'events') }}
GROUP BY user_id
In Materialize, materialized views are always up-to-date. You don’t need to manually refresh them!

View

Creates a non-materialized view. Results are computed on each query.
{{ config(materialized='view') }}

SELECT * FROM {{ ref('active_users') }}
WHERE event_count > 10

Table

Creates a materialized view (actual table support is pending).
{{ config(materialized='table') }}

SELECT * FROM {{ ref('user_segments') }}

Source

Creates a Materialize source using the custom source materialization.
-- models/sources/kafka_events.sql
{{ config(
    materialized='source',
    source_type='kafka'
) }}

CREATE SOURCE kafka_events
FROM KAFKA CONNECTION kafka_connection (
    TOPIC 'events'
)
FORMAT JSON

Sink

Creates a Materialize sink to export data.
{{ config(materialized='sink') }}

CREATE SINK user_sink
FROM {{ ref('active_users') }}
INTO KAFKA CONNECTION kafka_connection (
    TOPIC 'active-users'
)
FORMAT JSON

Ephemeral

Executes queries using CTEs. No database object is created.
{{ config(materialized='ephemeral') }}

SELECT * FROM events WHERE event_type = 'click'

Incremental (Not Supported)

Incremental models are not supported. Use materialized_view instead!Materialize’s materialized views provide the same benefits as dbt’s incremental models (processing only new data), but they’re always up-to-date without manual refreshes.

Indexes

Define indexes on your materializations using the indexes configuration:
{{ config(
    materialized='materialized_view',
    indexes=[
        {'columns': ['user_id']},
        {'columns': ['user_id', 'event_date'], 'name': 'user_date_idx'},
        {'default': True, 'cluster': 'analytics'}
    ]
) }}

SELECT 
    user_id,
    DATE(event_timestamp) as event_date,
    COUNT(*) as event_count
FROM {{ source('app', 'events') }}
GROUP BY user_id, event_date

Index Configuration Options

OptionTypeDescription
columnslistColumns to index. Use this OR default, not both.
namestringCustom index name. Auto-generated if not specified.
clusterstringCluster to create the index in. Defaults to model’s cluster.
defaultboolIf true, creates a default index on all columns.

Examples

-- Single column index
{{ config(indexes=[{'columns': ['user_id']}]) }}

-- Multi-column index with custom name
{{ config(indexes=[{'columns': ['user_id', 'timestamp'], 'name': 'user_time_idx'}]) }}

-- Default index (all columns)
{{ config(indexes=[{'default': True}]) }}

-- Index in specific cluster
{{ config(indexes=[{'columns': ['user_id'], 'cluster': 'analytics'}]) }}

Testing

dbt tests are fully supported:
# models/schema.yml
version: 2

models:
  - name: active_users
    description: "Users with recent activity"
    columns:
      - name: user_id
        description: "Unique user identifier"
        tests:
          - unique
          - not_null
      - name: event_count
        description: "Number of events"
        tests:
          - not_null
          - dbt_utils.expression_is_true:
              expression: ">= 0"

Storing Test Results

By default, test results are not stored. To store test failures:
# Store failures as materialized views
dbt test --store-failures

# Store failures as views (recommended for Materialize)
dbt test --store-failures --store-failures-as view
Or configure in dbt_project.yml:
tests:
  +store_failures: true
  +store_failures_as: view
  +schema: dbt_test_audit

Documentation

Generate and serve documentation:
# Generate documentation
dbt docs generate

# Serve documentation locally
dbt docs serve
Persist documentation to database:
models:
  my_project:
    +persist_docs:
      relation: true
      columns: true

Hooks

Pre- and post-hooks are fully supported:
{{ config(
    pre_hook="SET cluster = analytics_cluster",
    post_hook="GRANT SELECT ON {{ this }} TO analyst_role"
) }}

SELECT * FROM {{ ref('base_events') }}

Custom Schemas

Custom schemas work as expected in dbt:
{{ config(
    schema='analytics',
    materialized='materialized_view'
) }}

SELECT * FROM {{ ref('raw_events') }}
Read the dbt documentation on custom schemas to understand how schema naming works.

Additional Macros

Use the materialize-dbt-utils package for Materialize-specific macro implementations:
# packages.yml
packages:
  - package: materializeinc/materialize_dbt_utils
    version: 0.4.0
Install packages:
dbt deps

Common Commands

# Run all models
dbt run

# Run specific model
dbt run --select active_users

# Run models and downstream dependencies
dbt run --select active_users+

# Test models
dbt test

# Generate documentation
dbt docs generate

# Compile without running
dbt compile

Best Practices

1. Use Materialized Views

For most transformations, use materialized_view instead of table or incremental:
{{ config(materialized='materialized_view') }}

2. Index Strategically

Add indexes to columns used in JOINs and WHERE clauses:
{{ config(
    materialized='materialized_view',
    indexes=[{'columns': ['user_id', 'timestamp']}]
) }}

3. Use Clusters for Isolation

Separate development and production workloads:
dev:
  cluster: dev_cluster
prod:
  cluster: prod_cluster

4. Leverage Source Freshness Checks

Source freshness checks are not needed with Materialize because sources are always fresh — they continuously update as new data arrives.

5. Version Control Your Profiles

Use environment variables for sensitive data:
pass: "{{ env_var('DBT_MATERIALIZE_PASSWORD') }}"

Troubleshooting

Connection Issues

Verify your credentials:
dbt debug

Version Compatibility

Ensure you’re using Materialize v0.68.0+:
SELECT mz_version();

Slow Model Builds

Check cluster size and resource allocation:
SHOW CLUSTERS;
SHOW CLUSTER REPLICAS;

Example Project Structure

my_dbt_project/
├── dbt_project.yml
├── profiles.yml (in ~/.dbt/)
├── models/
│   ├── staging/
│   │   ├── stg_events.sql
│   │   └── stg_users.sql
│   ├── intermediate/
│   │   ├── int_user_events.sql
│   │   └── schema.yml
│   └── marts/
│       ├── active_users.sql
│       └── user_metrics.sql
├── tests/
└── macros/

Resources

Next Steps

Create Your First Model

Start building dbt models with Materialize

Learn About Materializations

Understand Materialize materialized views

Add Indexes

Optimize query performance with indexes

Testing in dbt

Write tests for your data transformations

Build docs developers (and LLMs) love