Metabase
Overview
Metabase is a business intelligence and analytics platform. Learn more in the official Metabase documentation.
The DataHub integration for Metabase covers BI entities such as dashboards, charts, datasets, and related ownership context. It also captures table-level lineage and stateful deletion detection.
Concept Mapping
| Source Concept | DataHub Concept | Notes |
|---|---|---|
"Metabase" | Data Platform | |
| Dashboard | Dashboard | |
| Card/Question | Chart | |
| Model | Dataset | SubTypes ["Metabase Model"]; transforming models also get "View" |
| Collection | Tag and Container | Tags optional; nested collections keep parent containers |
| Database Table | Dataset | From connected database |
| User | User (a.k.a CorpUser) | Ownership information |
Module metabase
Important Capabilities
| Capability | Status | Notes |
|---|---|---|
| Detect Deleted Entities | ✅ | Enabled by default via stateful ingestion. |
| Platform Instance | ✅ | Enabled by default. |
| Table-Level Lineage | ✅ | Supported by default for charts and dashboards. |
Overview
The metabase module ingests metadata from Metabase into DataHub. It is intended for production ingestion workflows and module-specific capabilities are documented below.
Prerequisites
To use this connector, you'll need:
- Metabase version v0.41+ (Models require v0.41+)
- Authentication credentials (either username/password or API key — API key is recommended)
- Appropriate permissions to access the Metabase API
Authentication
DataHub supports two authentication methods:
- API Key (Recommended) — more secure, no password management required. Generate one under Account Settings → API Keys in your Metabase instance.
- Username/Password
Install the Plugin
pip install 'acryl-datahub[metabase]'
Starter Recipe
Check out the following recipe to get started with ingestion! See below for full configuration options.
For general pointers on writing and running a recipe, see our main recipe guide.
source:
type: metabase
config:
# Coordinates
connect_uri: https://metabase.company.com
# Credentials (API key recommended)
api_key: "${METABASE_API_KEY}"
# Alternative: Username/Password authentication
# username: "${METABASE_USERNAME}"
# password: "${METABASE_PASSWORD}"
# Optional: Custom display URI (if connect_uri is only for ingestion)
# display_uri: https://metabase.company.com
# Feature flags
extract_collections_as_tags: true
extract_models: true
exclude_other_user_collections: false
# Optional: Custom platform mappings
# engine_platform_map:
# athena: glue
# sparksql: spark
# Optional: Database name overrides
# database_alias_map:
# postgres: my_postgres_db
# Optional: Platform instance mappings
# database_id_to_instance_map:
# "42": my_platform_instance
# platform_instance_map:
# clickhouse: my_clickhouse_cluster
# Default schema for SQL parsing
default_schema: public
sink:
# sink configs
Config Details
- Options
- Schema
Note that a . is used to denote nested fields in the YAML recipe.
| Field | Description |
|---|---|
api_key One of string(password), null | Metabase API key. If provided, the username and password will be ignored. Recommended method. Default: None |
connect_uri string | Metabase host URL. Default: http://localhost:3000 |
convert_lineage_urns_to_lowercase boolean | Whether to convert dataset (table) names to lowercase when creating lineage URNs. Column names always preserve their original case to match upstream source connectors. Most DataHub connectors (including Postgres, ClickHouse, BigQuery) preserve the original case from the database. Only set to true if your upstream connector explicitly lowercases table names (rare). Default: False |
convert_urns_to_lowercase boolean | Whether to convert dataset urns to lowercase. This value is part of each dataset's URN identity, so it must stay fixed for the life of a deployment. Changing it after data has been ingested re-keys every dataset (e.g. MyDb.MyTable becomes mydb.mytable); with stateful ingestion enabled the old-cased URNs are then soft-deleted as stale while the new-cased ones are created, producing duplicate or orphaned entities. Pick one value before the first run and leave it unchanged. Default: False |
database_alias_map One of string, null | Database name map to use when constructing dataset URN. Default: None |
database_id_to_instance_map One of string, null | Custom mappings between metabase database id and DataHub platform instance Default: None |
default_schema string | Default schema name to use when schema is not provided in an SQL query Default: public |
display_uri One of string, null | optional URL to use in links (if connect_uri is only for ingestion) Default: None |
engine_platform_map One of string, null | Custom mappings between metabase database engines and DataHub platforms Default: None |
exclude_other_user_collections boolean | Flag that if true, exclude other user collections Default: False |
extract_collections_as_tags boolean | Extract Metabase collections as tags on dashboards and charts Default: True |
extract_models boolean | Extract Metabase models (saved questions used as data sources) as datasets. Off by default: enabling it ingests models as dataset entities (and dashboards link to them as dataset edges) instead of charts, which changes their URNs. Default: False |
password One of string(password), null | Metabase password, used when an API key is not provided. Default: None |
platform_instance_map One of string, null | A holder for platform -> platform_instance mappings to generate correct dataset urns Default: None |
request_timeout_sec number | Timeout in seconds for each HTTP request to the Metabase API. Prevents ingestion from hanging indefinitely on an unresponsive server. Default: 30.0 |
username One of string, null | Metabase username, used when an API key is not provided. Default: None |
env string | The environment that all assets produced by this connector belong to Default: PROD |
stateful_ingestion One of StatefulStaleMetadataRemovalConfig, null | Default: None |
stateful_ingestion.enabled boolean | Whether or not to enable stateful ingest. Default: True if a pipeline_name is set and either a datahub-rest sink or datahub_api is specified, otherwise False Default: False |
stateful_ingestion.fail_safe_threshold number | Prevents large amount of soft deletes & the state from committing from accidental changes to the source configuration if the relative change percent in entities compared to the previous state is above the 'fail_safe_threshold'. Default: 75.0 |
stateful_ingestion.remove_stale_metadata boolean | Soft-deletes the entities present in the last successful run but missing in the current run with stateful_ingestion enabled. Default: True |
The JSONSchema for this configuration is inlined below.
{
"$defs": {
"StatefulStaleMetadataRemovalConfig": {
"additionalProperties": false,
"description": "Base specialized config for Stateful Ingestion with stale metadata removal capability.",
"properties": {
"enabled": {
"default": false,
"description": "Whether or not to enable stateful ingest. Default: True if a pipeline_name is set and either a datahub-rest sink or `datahub_api` is specified, otherwise False",
"title": "Enabled",
"type": "boolean"
},
"remove_stale_metadata": {
"default": true,
"description": "Soft-deletes the entities present in the last successful run but missing in the current run with stateful_ingestion enabled.",
"title": "Remove Stale Metadata",
"type": "boolean"
},
"fail_safe_threshold": {
"default": 75.0,
"description": "Prevents large amount of soft deletes & the state from committing from accidental changes to the source configuration if the relative change percent in entities compared to the previous state is above the 'fail_safe_threshold'.",
"maximum": 100.0,
"minimum": 0.0,
"title": "Fail Safe Threshold",
"type": "number"
}
},
"title": "StatefulStaleMetadataRemovalConfig",
"type": "object"
}
},
"additionalProperties": false,
"properties": {
"convert_urns_to_lowercase": {
"default": false,
"description": "Whether to convert dataset urns to lowercase. This value is part of each dataset's URN identity, so it must stay fixed for the life of a deployment. Changing it after data has been ingested re-keys every dataset (e.g. `MyDb.MyTable` becomes `mydb.mytable`); with stateful ingestion enabled the old-cased URNs are then soft-deleted as stale while the new-cased ones are created, producing duplicate or orphaned entities. Pick one value before the first run and leave it unchanged.",
"title": "Convert Urns To Lowercase",
"type": "boolean"
},
"stateful_ingestion": {
"anyOf": [
{
"$ref": "#/$defs/StatefulStaleMetadataRemovalConfig"
},
{
"type": "null"
}
],
"default": null
},
"env": {
"default": "PROD",
"description": "The environment that all assets produced by this connector belong to",
"title": "Env",
"type": "string"
},
"platform_instance_map": {
"anyOf": [
{
"additionalProperties": {
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "A holder for platform -> platform_instance mappings to generate correct dataset urns",
"title": "Platform Instance Map"
},
"connect_uri": {
"default": "http://localhost:3000",
"description": "Metabase host URL.",
"title": "Connect Uri",
"type": "string"
},
"display_uri": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "optional URL to use in links (if `connect_uri` is only for ingestion)",
"title": "Display Uri"
},
"username": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Metabase username, used when an API key is not provided.",
"title": "Username"
},
"password": {
"anyOf": [
{
"format": "password",
"type": "string",
"writeOnly": true
},
{
"type": "null"
}
],
"default": null,
"description": "Metabase password, used when an API key is not provided.",
"title": "Password"
},
"api_key": {
"anyOf": [
{
"format": "password",
"type": "string",
"writeOnly": true
},
{
"type": "null"
}
],
"default": null,
"description": "Metabase API key. If provided, the username and password will be ignored. Recommended method.",
"title": "Api Key"
},
"request_timeout_sec": {
"default": 30.0,
"description": "Timeout in seconds for each HTTP request to the Metabase API. Prevents ingestion from hanging indefinitely on an unresponsive server.",
"exclusiveMinimum": 0,
"title": "Request Timeout Sec",
"type": "number"
},
"database_alias_map": {
"anyOf": [
{
"additionalProperties": {
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Database name map to use when constructing dataset URN.",
"title": "Database Alias Map"
},
"engine_platform_map": {
"anyOf": [
{
"additionalProperties": {
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Custom mappings between metabase database engines and DataHub platforms",
"title": "Engine Platform Map"
},
"database_id_to_instance_map": {
"anyOf": [
{
"additionalProperties": {
"type": "string"
},
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Custom mappings between metabase database id and DataHub platform instance",
"title": "Database Id To Instance Map"
},
"default_schema": {
"default": "public",
"description": "Default schema name to use when schema is not provided in an SQL query",
"title": "Default Schema",
"type": "string"
},
"exclude_other_user_collections": {
"default": false,
"description": "Flag that if true, exclude other user collections",
"title": "Exclude Other User Collections",
"type": "boolean"
},
"extract_collections_as_tags": {
"default": true,
"description": "Extract Metabase collections as tags on dashboards and charts",
"title": "Extract Collections As Tags",
"type": "boolean"
},
"extract_models": {
"default": false,
"description": "Extract Metabase models (saved questions used as data sources) as datasets. Off by default: enabling it ingests models as `dataset` entities (and dashboards link to them as dataset edges) instead of charts, which changes their URNs.",
"title": "Extract Models",
"type": "boolean"
},
"convert_lineage_urns_to_lowercase": {
"default": false,
"description": "Whether to convert dataset (table) names to lowercase when creating lineage URNs. Column names always preserve their original case to match upstream source connectors. Most DataHub connectors (including Postgres, ClickHouse, BigQuery) preserve the original case from the database. Only set to true if your upstream connector explicitly lowercases table names (rare).",
"title": "Convert Lineage Urns To Lowercase",
"type": "boolean"
}
},
"title": "MetabaseConfig",
"type": "object"
}
Capabilities
Lineage
The connector extracts lineage across all Metabase query types:
Native SQL
SQL queries are parsed with DataHub's SQLGlot-based parser to extract table references, including those inside JOIN clauses and subqueries. Metabase template variables ({{variable}}, [[WHERE ...]]) are stripped before parsing.
Query Builder (MBQL)
Questions and models built with Metabase's visual query builder store their logic as MBQL — a structured JSON representation. The connector resolves MBQL to upstream database tables and, for models, also produces column-level lineage:
- Table-level: The
source-tablefield and alljoins[].source-tableentries are resolved to DataHub dataset URNs, covering multi-table join scenarios. - Column-level (models only):
result_metadata[].field_refrecords which MBQL expression produced each output column. The connector resolves these refs to upstream field URNs via/api/field/{id}:["field", id, ...]— direct pass-through column["expression", name]— calculated column; traces back throughquery.expressions["aggregation", index]— metric column; traces back throughquery.aggregation.COUNT(*)with no explicit field fans-in all resolved upstream columns (matching Tableau lineage behaviour).
Nested Queries
Charts or models that reference other cards (source-table: "card__456") are recursively resolved to their ultimate source tables (max depth: 5, to guard against circular references).
Dashboard Lineage
Table dependencies from all charts in a dashboard are rolled up into direct table-to-dashboard lineage edges, deduplicating tables referenced by multiple charts.
Collection Tags
Metabase Collections are mapped to DataHub tags:
- Tag format:
metabase_collection_{sanitized_name}(e.g. "Sales & Marketing" →metabase_collection_sales_marketing) - Tags are applied to dashboards, charts, and models within that collection
- Disable with
extract_collections_as_tags: false
Database and Platform Mapping
Metabase databases are mapped to a DataHub platform based on the engine field returned by /api/database. Override with engine_platform_map:
engine_platform_map:
athena: glue
DataHub determines the database name from the same API response. Override with database_alias_map:
database_alias_map:
postgres: my_custom_db_name
Platform Instance Mapping
When multiple instances of the same platform exist in DataHub (for example, two ClickHouse clusters), map Metabase database IDs to platform instances with database_id_to_instance_map:
database_id_to_instance_map:
"42": platform_instance_in_datahub
The key must be a string, not an integer.
If database_id_to_instance_map is not set, platform_instance_map is used as a fallback. If neither is set, platform instance is omitted from dataset URNs.
Filtering Collections
To exclude collections owned by other users:
exclude_other_user_collections: true
Limitations
- Column-level lineage is only available for cards saved as a Model (type
"model"). Regular MBQL questions do not expose reliablefield_refdata. - Template variables in native SQL queries (
{{variable}},[[optional clause]]) are stripped before parsing, so lineage based on dynamic table references may be incomplete. - Circular card references are cut off at depth 5; deeply nested card chains beyond that limit will not have lineage extracted.
Troubleshooting
If ingestion fails, validate credentials, permissions, connectivity, and scope filters first. Then review ingestion logs for source-specific errors and adjust configuration accordingly.
- Auth failure: ensure the API key or username/password is correct and the account has access to the relevant collections.
- Missing lineage: check that
extract_models: trueis set and that the cards are saved as Models in Metabase. - Unknown platform warnings: add an entry to
engine_platform_mapfor the unrecognised engine name.
Code Coordinates
- Class Name:
datahub.ingestion.source.metabase.source.MetabaseSource - Browse on GitHub
If you've got any questions on configuring ingestion for Metabase, feel free to ping us on our Slack.
This page is auto-generated from the underlying source code. To make changes, please edit the relevant source files in the metadata-ingestion directory.
Tip: For quick typo fixes or documentation updates, you can click the ✏️ Edit icon directly in the GitHub UI to open a Pull Request. For larger changes and PR naming conventions, please refer to our Contributing Guide.