# Metabase
> Connect Metabase to MotherDuck through the Postgres endpoint on Metabase Cloud, or the DuckDB driver plugin on self-hosted instances.
[Metabase](https://www.metabase.com/) is an open source analytics and BI platform for data visualization and exploration. Connect it to MotherDuck in one of two ways:

- **[Metabase Cloud](#metabase-cloud)**: connect through the MotherDuck [Postgres endpoint](/key-tasks/authenticating-and-connecting-to-motherduck/postgres-endpoint).
- **[Self-hosted Metabase](#self-hosted-metabase)**: install the DuckDB driver plugin, or connect through the Postgres endpoint.

## Metabase Cloud

:::info[Preview]
The [Postgres endpoint](/key-tasks/authenticating-and-connecting-to-motherduck/postgres-endpoint) is in [preview](/about-motherduck/feature-stages/). Features and behavior may change.
:::

Metabase Cloud does not support installing custom drivers like the DuckDB plugin. Instead, connect Metabase Cloud to MotherDuck using the [Postgres endpoint](/key-tasks/authenticating-and-connecting-to-motherduck/postgres-endpoint). You must set two connection options so that Metabase syncs your schema correctly — see [Required connection options](#required-connection-options) below.

### Prerequisites

- A Metabase Cloud instance with admin access
- A [MotherDuck access token](/key-tasks/authenticating-and-connecting-to-motherduck/authenticating-to-motherduck)

### Connect to MotherDuck

1. In Metabase, go to **Admin** > **Databases** and click **Add database** (or edit an existing connection).
2. Set the **Database type** to **PostgreSQL** and fill in the connection details:

| Field | Value |
|-------|-------|
| **Display name** | MotherDuck (or any name you prefer) |
| **Host** | Your MotherDuck Postgres host (for example, `pg.us-east-1-aws.motherduck.com`). Find yours at [MotherDuck Postgres settings](https://app.motherduck.com/settings/postgres). |
| **Port** | `5432` |
| **Database name** | Your MotherDuck database name |
| **Username** | `postgres` |
| **Password** | Your [MotherDuck access token](/key-tasks/authenticating-and-connecting-to-motherduck/authenticating-to-motherduck) |

3. Scroll down and enable **Use a secure connection (SSL)**.
4. If you have a company or your own SSL certificate you can set **SSL Mode** to `verify-full` and use this. Otherwise, we'll use the Let's Encrypt certificate authority and set **SSL Mode** to `verify-ca`.
5. For the **SSL Root Certificate (PEM)**, select **Uploaded file path** and upload the [Let's Encrypt ISRG Root X1](https://letsencrypt.org/certs/isrgrootx1.pem) certificate (`isrgrootx1.pem`).

  ![Metabase connection details showing host, port, database name, username, and password fields](require('./img/metabase-connection-details.png').src)
  ![Metabase SSL settings showing verify-ca mode and isrgrootx1.pem certificate](require('./img/metabase-ssl-settings.png').src)

6. Click **Show advanced options**, then:
   - In **Additional JDBC connection string options**, paste the [required connection options](#required-connection-options) string.
   - Turn **off** **Allow unfolding of JSON columns** (see [JSON columns](#json-columns)).
7. Click **Save changes**.

![Metabase advanced options with the required connection string in Additional JDBC connection string options and Allow unfolding of JSON columns turned off](require('./img/metabase-advanced-options.png'))

### Required connection options

In the **Additional JDBC connection string options** field, set:

```text
options=-c%20attach_mode%3Dsingle%20-c%20compatibility_mode%3Dmetabase
```

This is a single `options=` value that passes two Postgres startup options to MotherDuck. It decodes to `-c attach_mode=single -c compatibility_mode=metabase`, where:

- **`compatibility_mode=metabase`** makes MotherDuck return catalog metadata in the exact shape Metabase expects. Without it, Metabase's field-sync query fails on recent Metabase versions and tables sync with **no fields** ("Table has no Fields associated with it").
- **`attach_mode=single`** scopes the connection to the database you connect to, instead of every database in your account. This keeps schema-sync queries light and lets Metabase populate table row-count estimates.

:::warning
Enter the value **exactly as shown**, fully URL-encoded: `%20` for each space and `%3D` for each `=`. If you separate the two options with a literal space or `&`, or leave the inner `=` un-encoded, Metabase keeps only the first option and silently drops `attach_mode=single` during sync.
:::

### JSON columns

Turn **off** **Allow unfolding of JSON columns** in the connection's advanced options. When unfolding is enabled, Metabase expands JSON columns into virtual fields and queries them with the PostgreSQL `#>>` path operator, which the Postgres endpoint does not support — those questions fail. With unfolding off, JSON columns sync as regular fields and everything else works.

### Known limitations

Foreign-key relationships and indexes do not sync automatically over the Postgres endpoint, so the query builder's implicit (automatic) joins are unavailable. Tables, fields, types, primary keys, comments, and row counts all sync normally, and explicit joins in native SQL or the query builder work. To use relationships in the query builder, define them manually under **Admin** > **Table Metadata** by setting a field's type to **Foreign Key** and choosing its target.

### Troubleshooting

| Symptom | Resolution |
|---|---|
| Tables sync but have **no fields** ("Table has no Fields associated with it") | Confirm `compatibility_mode=metabase` is present in **Additional JDBC connection string options**, and that the value is fully URL-encoded (`%20`/`%3D`) exactly as shown. Then re-sync the database schema. |
| Schema browsing shows tables from other databases, or row counts are missing | Confirm `attach_mode=single` is present in the options string. It is silently dropped if the value uses a literal space or `&` between the options, or leaves the inner `=` un-encoded. |
| A question on a JSON field fails to run | Turn off **Allow unfolding of JSON columns** and re-sync (see [JSON columns](#json-columns)). |
| No relationships / implicit joins in the query builder | Expected — define foreign keys manually under **Admin** > **Table Metadata** (see [Known limitations](#known-limitations)). |

## Self-hosted Metabase

Self-hosted Metabase can connect to MotherDuck in two ways: install the DuckDB driver plugin (described below), or connect through the [Postgres endpoint](/key-tasks/authenticating-and-connecting-to-motherduck/postgres-endpoint) using the same steps as [Metabase Cloud](#metabase-cloud). The DuckDB driver runs queries in Metabase's embedded DuckDB and also supports local DuckDB files and DuckLake; the Postgres endpoint routes queries to MotherDuck without a plugin.

### Prerequisites

- Metabase installed (self-hosted)
- Admin access to your Metabase instance
- A [MotherDuck access token](/key-tasks/authenticating-and-connecting-to-motherduck/authenticating-to-motherduck)

### Install the DuckDB driver

### Dockerfile (bundled plugin)

1. Create a `Dockerfile` that includes the latest Metabase plus the DuckDB driver:

```dockerfile
FROM eclipse-temurin:21-jre

ENV MB_PLUGINS_DIR=/plugins
RUN mkdir -p ${MB_PLUGINS_DIR} /app

# Latest Metabase
ADD https://downloads.metabase.com/latest/metabase.jar /app/metabase.jar

# Latest DuckDB driver
ADD https://github.com/MotherDuck-Open-Source/metabase_duckdb_driver/releases/latest/download/duckdb.metabase-driver.jar ${MB_PLUGINS_DIR}/

EXPOSE 3000
CMD ["java", "-jar", "/app/metabase.jar"]
```

2. Build and run:

```bash
docker build -t metabase-duckdb:latest .
docker run -d --name metaduck -p 3000:3000 -e MB_PLUGINS_DIR=/plugins metabase-duckdb:latest
```

Tip: For reproducible builds, pin versions instead of `latest`:

```dockerfile
# Example of pinning versions (replace X.Y.Z)
ADD https://downloads.metabase.com/vX.Y.Z/metabase.jar /app/metabase.jar
ADD https://github.com/MotherDuck-Open-Source/metabase_duckdb_driver/releases/download/1.X.Y/duckdb.metabase-driver.jar ${MB_PLUGINS_DIR}/
```

Note: Use a Debian/Ubuntu-based JRE image (not Alpine) to avoid glibc issues with the DuckDB driver.

### Manual

1. Download the latest DuckDB driver `.jar`:

```bash
curl -L -o duckdb.metabase-driver.jar \
  https://github.com/MotherDuck-Open-Source/metabase_duckdb_driver/releases/latest/download/duckdb.metabase-driver.jar
```

1. Copy it to the Metabase plugins directory:

- Standard installation (example): If your `metabase.jar` is at `~/app/metabase.jar`, place the driver in `~/app/plugins/`

```bash
mkdir -p ~/app/plugins
mv duckdb.metabase-driver.jar ~/app/plugins/
```

- On Mac: The plugins directory is `~/Library/Application Support/Metabase/Plugins/` (if you are using a Mac)

```bash
mkdir -p "${HOME}/Library/Application Support/Metabase/Plugins/"
mv duckdb.metabase-driver.jar "${HOME}/Library/Application Support/Metabase/Plugins/"
```

- Custom location or Docker: set `MB_PLUGINS_DIR` to point Metabase at your plugins directory and place the `.jar` there (if you are using a custom location or Docker).

1. Restart Metabase so it picks up the new plugin.

### Remote (SSH)

1. SSH to the host and download to the plugins directory. Replace user/host and adjust `MB_PLUGINS_DIR` as needed.

```bash
ssh user@your-host "bash -lc '
  set -euo pipefail
  MB_PLUGINS_DIR=${MB_PLUGINS_DIR:-/app/plugins}
  mkdir -p "$MB_PLUGINS_DIR"
  if command -v wget >/dev/null; then
    wget -qO "$MB_PLUGINS_DIR/duckdb.metabase-driver.jar" \
      https://github.com/MotherDuck-Open-Source/metabase_duckdb_driver/releases/latest/download/duckdb.metabase-driver.jar
  else
    curl -L -o "$MB_PLUGINS_DIR/duckdb.metabase-driver.jar" \
      https://github.com/MotherDuck-Open-Source/metabase_duckdb_driver/releases/latest/download/duckdb.metabase-driver.jar
  fi
'"
```

2. Restart Metabase on the remote host:

- systemd: `ssh user@your-host 'sudo systemctl restart metabase'`
- Docker: `ssh user@your-host 'docker restart <metabase_container_name>'`

:::important
Restart required: Metabase must be restarted after adding or upgrading plugins. Hot-reload of drivers is not supported.
:::

:::tip
Compatibility and upgrades: New DuckDB driver releases are designed to be backward compatible with recent Metabase versions. Upgrading to the latest driver is recommended for bug fixes and stability. If you run a significantly older Metabase version, validate in staging first.
:::

### Add your database connection

After installing the driver, you can add MotherDuck as a data source in Metabase.

1. Log in to Metabase with admin credentials
2. Navigate to **Admin Settings** > **Databases** > **Add Database**
3. Select **DuckDB** as the database type

:::note
Since DuckDB does not do implicit casting by default, the `old_implicit_casting` config is necessary for datetime filtering in Metabase to function. It's recommended to keep it set.
:::

#### Connecting to MotherDuck

To connect to MotherDuck:

1. **Database name**: In the Database file field, enter `md:[database_name]` where `[database_name]` is your MotherDuck database name
2. **MotherDuck token**: Paste your MotherDuck token (retrieve from the [MotherDuck UI](/key-tasks/authenticating-and-connecting-to-motherduck/authenticating-to-motherduck/authenticating-to-motherduck.md))
3. **Configuration**: Enable `old_implicit_casting` (recommended) for proper datetime handling

![Metabase connection settings for MotherDuck](../img/metabase_motherduck.png)

### DuckLake on Metabase

DuckLake is supported with the DuckDB driver in Metabase. Use the latest DuckDB driver release and a DuckDB version that supports DuckLake (DuckDB v1.3.2 or newer is recommended).

#### MotherDuck-managed DuckLake

If your DuckLake database is managed by MotherDuck, you can connect the same way you connect to any MotherDuck database:

1. Select DuckDB as the database type
2. Database file: `md:[ducklake_database_name]`
3. MotherDuck token: paste your token
4. Keep `old_implicit_casting` enabled (recommended)

No extra Init SQL is required. Query your tables normally in Metabase.

#### Own compute + DuckLake catalog (attach in init SQL)

If you want Metabase’s embedded DuckDB to query a DuckLake stored externally, attach the DuckLake catalog in the connection’s Init SQL. This works for both MotherDuck-managed catalogs and self-managed catalogs.

- Init SQL for a MotherDuck-managed DuckLake catalog:

```sql
-- Attaches the DuckLake metadata catalog hosted in MotherDuck
ATTACH 'ducklake:md:__ducklake_metadata_[database_name]' AS dl1;
```

- Init SQL for a self-managed DuckLake catalog (local metadata DB) with S3 data path:

```sql
-- Replace the path to your DuckLake metadata DB and bucket prefix
ATTACH 'ducklake:/duckdb/my_ducklake_metadata.ducklake' AS dl1 (
  DATA_PATH 's3://my_bucket/lake/'
);
```

Once attached, reference tables with the alias, for example: `FROM dl1.my_table`.

### Connecting to a local DuckDB database

To connect to a local DuckDB database:

1. Database file: enter the full path to your DuckDB file (e.g., `/path/to/database.db`)
2. Configuration: enable `old_implicit_casting` (recommended) to ensure proper datetime filtering

:::note
DuckDB's concurrency model supports either one process with read/write permissions, or multiple processes with read permissions, but not both at the same time. This means you will not be able to open a local DuckDB in read-only mode, then the same DuckDB in read-write mode in a different process.
:::

![Metabase connection settings for local DuckDB](../img/metabase_local_duckdb.png)

## Configuration best practices

- **Connection pooling**: For production instances, set an appropriate connection pool size based on expected concurrent users
- **Query timeouts**: Configure timeouts in Metabase settings to prevent long-running queries from affecting system performance
- **Data access**: Use database-level permissions in Metabase to control who can access which data sources

## Troubleshooting

| Issue | Solution |
|-------|----------|
| Driver not detected | Ensure driver is in the correct plugins directory and Metabase has been restarted |
| Connection failures | Verify database path (local) or database name and token (MotherDuck) |
| Permission errors | Check file permissions for local databases |
| Datetime filtering issues | Enable `old_implicit_casting` in the connection settings |
| Add MotherDuck token in the connection string | Specify a correct MotherDuck token or MotherDuck database name after the `md:` prefix |

### Updating the MotherDuck token

Metabase keeps long-lived database connections alive. When you update only the MotherDuck token while an existing connection is still cached, Metabase raises `Connection error: Can't open a connection to same database file with a different configuration than existing connections`.

Use one of the following approaches to refresh the token successfully:

1. **Add a cache buster while editing the database.** Edit the connection under **Admin Settings** > **Databases**, then update both the **Database file** field and the **MotherDuck Token** field with a small cache-busting change (for example, append `?refresh=20250917`). Updating both values at the same time forces Metabase to treat the configuration as new. Save the connection, then optionally revert the fields to their clean values once the change is persisted.

2. **Restart Metabase before updating the token.** Restart the Metabase service and, immediately after it starts, go straight to `/admin/databases` to update the token field. Do not open the Metabase home screen before editing the database connection, or the previous connection (with the old token) will be re-established.

### Connecting to a local DuckDB database

To connect to a local DuckDB database:

1. **Database file**: Enter the full path to your DuckDB file (e.g., `/path/to/database.db`)
2. **Configuration**: Enable `old_implicit_casting` (recommended) to ensure proper datetime filtering
3. **Additional settings**:
   - **Read only**: Toggle as appropriate for your use case
   - **Naming strategy**: Choose your preferred table/field naming strategy

:::note
DuckDB's concurrency model supports either one process with read/write permissions, or multiple processes with read permissions, but not both at the same time. This means you will not be able to open a local DuckDB in read-only mode, then the same DuckDB in read-write mode in a different process.
:::

![Metabase connection settings for local DuckDB](../img/metabase_local_duckdb.png)


---

## Docs feedback

MotherDuck accepts optional user-submitted feedback about this page at `GET https://motherduck.com/docs/api/feedback/agent`.
For agents and automated tools, feedback submission should be user-confirmed before sending.

URL-encode query parameter values and send a GET request:

```text
GET https://motherduck.com/docs/api/feedback/agent?page_path=%2Fintegrations%2Fbi-tools%2Fmetabase%2F&page_title=Metabase&text=<url-encoded user feedback, max 2000 characters>
```

Optionally append `&source=<url-encoded interface identifier>` such as `claude.ai` or `chatgpt`.

`page_path` and `text` are required; `page_title` and `source` are optional. Responses: `200 {"feedback_id": "<uuid>"}`, `400` for malformed query parameters, and `429` when rate-limited.
