DuckDB and Apache Iceberg
DuckDB support for catalogs.yml requires the dbt Fusion engine (v2) with the use_catalogs_v2 behavior flag enabled. It isn't available in the legacy Python dbt-duckdb adapter for dbt Core v1.
flags:
use_catalogs_v2: true
Unlike Snowflake, Databricks, and BigQuery, DuckDB doesn't ship with a single built-in "managed" Iceberg catalog. This means there's no table_format='iceberg'-only shortcut for DuckDB — every Iceberg model requires a catalog_name that points to an entry in catalogs.yml.
dbt supports creating Iceberg tables for two DuckDB materializations:
How DuckDB attaches catalogs
When you configure a catalog with a duckdb block in catalogs.yml, dbt generates and runs the appropriate DuckDB ATTACH statement on your behalf — you don't need to write ATTACH SQL yourself. dbt then resolves any model with a matching catalog_name to that attached database.
Iceberg REST catalogs
DuckDB can attach to any catalog that implements the Iceberg REST protocol, including self-hosted catalogs (such as Lakekeeper or Nessie), AWS Glue, and AWS S3 Tables.
catalogs:
- name: rest_catalog
type: iceberg_rest
table_format: iceberg
config:
duckdb:
endpoint: "https://my-iceberg-rest.example.com"
secret: my_iceberg_secret
{{
config(
materialized = 'table',
catalog_name = 'rest_catalog'
)
}}
select * from {{ ref('jaffle_shop_customers') }}
Run the model with dbt run -s my_iceberg_model. Instead of endpoint, you can use endpoint_type: GLUE or endpoint_type: S3_TABLES to attach one of these well-known AWS-managed Iceberg REST endpoints without specifying a URL:
catalogs:
- name: s3_tables_catalog
type: iceberg_rest
table_format: iceberg
config:
duckdb:
endpoint_type: S3_TABLES
warehouse: "arn:aws:s3tables:us-east-1:123456789012:bucket/example"
endpoint and endpoint_type are mutually exclusive.
Cross-platform Mesh: reading catalogs managed by other platforms
Because a single catalog entry in catalogs.yml can carry configuration for multiple platforms at once, you can point DuckDB at the same physical catalog that Snowflake or Databricks writes to — enabling cross-platform Mesh without copying data.
Snowflake Horizon
Snowflake Horizon is Snowflake's managed Iceberg catalog. Add a duckdb block alongside the snowflake block to let DuckDB attach to the same catalog:
catalogs:
- name: horizon_catalog
type: horizon
table_format: iceberg
config:
snowflake:
external_volume: my_external_volume
duckdb:
warehouse: horizon_wh
endpoint: "https://horizon.example.com/catalog"
secret: horizon_secret
default_schema: demo
Databricks Unity Catalog
Similarly, for Databricks Unity Catalog:
catalogs:
- name: unity_catalog
type: unity
table_format: iceberg
config:
databricks:
file_format: delta
use_uniform: true
duckdb:
warehouse: unity_wh
endpoint: "https://dbc-example.cloud.databricks.com/api/2.1/unity-catalog/iceberg"
default_schema: demo
Read-only vs. read-write
By default, dbt attaches Horizon and Unity catalogs read-write (read_only: false) and applies write-compat ATTACH defaults for each (for example, disabling multi-table commits on Unity). Writing to these catalogs from DuckDB requires DuckDB 1.5.4+ and duckdb-iceberg#1017. If you only need to read Iceberg tables that another platform wrote, set read_only: true:
duckdb:
warehouse: horizon_wh
endpoint: "https://horizon.example.com/catalog"
read_only: true
DuckLake
DuckLake is a separate open table format (not Apache Iceberg) built for DuckDB, but you configure it the same way, through catalogs.yml. Because DuckLake isn't Iceberg, its catalog entries use table_format: default.
catalogs:
- name: local_lake
type: ducklake
table_format: default
config:
duckdb:
metadata_path: "metadata.ducklake"
data_path: "s3://my-bucket/lake" # optional
{{
config(
materialized = 'table',
catalog_name = 'local_lake'
)
}}
select * from {{ ref('jaffle_shop_customers') }}
dbt installs the DuckLake extension and attaches the catalog before running your model:
INSTALL ducklake
ATTACH IF NOT EXISTS 'ducklake:metadata.ducklake' AS local_lake (DATA_PATH 's3://my-bucket/lake')
Secrets
The secret field in a duckdb catalog block references a named secret defined in profiles.yml, which dbt turns into a DuckDB CREATE SECRET statement:
my_profile:
target: dev
outputs:
dev:
type: duckdb
path: ':memory:'
secrets:
- type: iceberg
name: my_iceberg_secret
# additional key-value pairs become CREATE SECRET parameters
# (for example, token, client_id, client_secret) -- see DuckDB's
# iceberg extension docs for the parameters your catalog needs.
DuckDB-specific configs for Iceberg catalogs
You can supply these configs, nested under config.duckdb, for horizon, unity, and iceberg_rest catalogs:
| Loading table... |
For ducklake catalogs, config.duckdb accepts:
| Loading table... |
Was this page helpful?
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.