---
title: Connect a warehouse
description: Connect Postgres, BigQuery, Snowflake, Redshift, Databricks, SQL Server, or Oracle Database, and see which a refreshing dashboard can publish against today.
updated: 2026-09-22
tier: pro
engines: [postgres, bigquery, snowflake, redshift, databricks, sqlserver, oracle]
---

A dashboard can only refresh if there is something to re-query. Connecting a
warehouse is what turns a one-time dashboard into one that re-runs its own SQL on
a schedule with no AI in the loop.

Dashies calls these **data sources** in the app. The wire format, the API fields,
and the error strings call the same thing a **connection**, so both words appear
on these pages.

:::warning{title="Warehouse connections are a paid feature"}
Creating, testing, and resyncing a data source all require a paid plan. See
[what is gated](/concepts/plans-and-gating#what-is-gated). The
refusal is `403` with `A paid plan is required to connect a warehouse.` Deleting,
renaming, and rotating an existing one are not gated. A plan that is trialing
counts as paid.
:::

:::note{title="Which engines a refreshing dashboard can publish against today"}
All seven engines can be connected, tested, introspected, and queried while
authoring. Which of them a **refreshing** dashboard can publish against is read
out of the database as each publish is judged, so the refusal names the current
set and this sentence names a reading: **on 2026-09-18 it was BigQuery,
Databricks, Oracle Database, Postgres, Snowflake and SQL Server**. Redshift
connects, explores and validates, but a publish against it is refused. Believe
the refusal over this date.
:::

## Pick your engine

The **Refreshing dashboard** column says the same thing as the note, per engine.

| Engine | Refreshing dashboard | What it connects to | Page |
|---|---|---|---|
| PostgreSQL | yes | Postgres or a Postgres-compatible warehouse. | [Postgres](/guides/connect-warehouse/postgres) |
| BigQuery | yes | Google BigQuery, via a service account. | [BigQuery](/guides/connect-warehouse/bigquery) |
| Snowflake | yes | Snowflake, via key-pair authentication. | [Snowflake](/guides/connect-warehouse/snowflake) |
| Amazon Redshift | not yet | Amazon Redshift, via the Data API. | [Redshift](/guides/connect-warehouse/redshift) |
| Databricks | yes | Databricks SQL Warehouse, via the Statement Execution API. | [Databricks](/guides/connect-warehouse/databricks) |
| SQL Server | yes | Microsoft SQL Server or Azure SQL. | [SQL Server](/guides/connect-warehouse/sqlserver) |
| Oracle Database | yes | Oracle Database, self-managed or Autonomous. | [Oracle Database](/guides/connect-warehouse/oracle) |

## A warehouse dashboard is served

A dashboard that reads a warehouse keeps its data with Dashies rather than inside
the published file. Each refresh runs your SQL, writes the result to Dashies' own
storage, and the page asks for what it needs when a reader opens it. Every engine
the roster admits is on that path; there is no second shape to choose between.

The in-file materializations - `cube`, `lattice`, `hybrid` and `rows`, which ship
the numbers inside the page - are not available on a warehouse you connect,
because nothing already inside a file can be filtered per reader. A publish that
asks for one on your own warehouse connection is refused, and the refusal says so.

There is one exception, and it is about a plan rather than about a connection: the shared
**Dashies sample data** connection is a warehouse too, and on a **free** plan a dashboard
built on it carries its numbers inside the page instead. On a trial or a paid plan the same
dashboard keeps its data with Dashies like any other warehouse dashboard. Nothing about who
can read the sample changes either way.

One consequence to design for: **the refresh is asynchronous.** A newly published
dashboard reads "Updating" until the first refresh lands, rather than carrying
numbers from the moment you published.

## What to have ready

Pick your engine below. The tab you choose is remembered across this whole site,
so every other page with engine tabs will open on the same one.

::::tabs{sync=engine}

:::tab{label="PostgreSQL"}
- A host reachable from the public internet, on port 5432, 5433, or 6543.
- The database name and the schemas you want to import.
- A read-only login. The connect form gives you the exact SQL to create one.

If the database only accepts connections from known addresses, tick that on the connect form
and allow the two addresses it shows. Full page:
[Connect PostgreSQL](/guides/connect-warehouse/postgres).
:::

:::tab{label="BigQuery"}
- The BigQuery project id Dashies reads from.
- A dedicated service account with BigQuery access, and its JSON key.

You paste the service account email and the private key separately, not the whole
JSON file. Full page: [Connect BigQuery](/guides/connect-warehouse/bigquery).
:::

:::tab{label="Snowflake"}
- Your account identifier in the hyphenated form, for example `myorg-myaccount`.
  The dotted org form is rejected.
- A dedicated read-only user with an RSA public key registered on it, and the
  matching PKCS#8 private key.
- The warehouse Dashies should run refresh queries on.

Full page: [Connect Snowflake](/guides/connect-warehouse/snowflake).
:::

:::tab{label="Amazon Redshift"}
- The AWS region, plus either a Serverless workgroup name or a provisioned
  cluster identifier.
- An IAM access key pair whose policy allows the Redshift Data API.
- A Secrets Manager ARN holding the read-only database user.

Two separate credentials. Full page:
[Connect Redshift](/guides/connect-warehouse/redshift).
:::

:::tab{label="Databricks"}
- Your workspace host, for example `dbc-xxxxxxxx-xxxx.cloud.databricks.com`.
- The SQL warehouse id.
- A service principal's client id and OAuth client secret.

Full page: [Connect Databricks](/guides/connect-warehouse/databricks).
:::

:::tab{label="SQL Server"}
- A host reachable from the public internet, on port 1433. If its firewall admits
  only known addresses, allow your region's two on
  [IP addresses](/reference/ip-addresses).
- The database name and the schemas you want to import.
- A read-only SQL login. This is a requirement, not a recommendation: the test
  refuses a login that can write.
- TLS with a certificate that validates. A self-signed certificate cannot
  connect.

Full page: [Connect SQL Server](/guides/connect-warehouse/sqlserver).
:::

:::tab{label="Oracle Database"}
- A host reachable from the public internet, on port 1521, 1522, or 2484. If it
  admits only known addresses, allow your region's two on
  [IP addresses](/reference/ip-addresses).
- The **service name** the listener registers, not a SID.
- A read-only database user, and the schemas you want to import. Oracle schemas
  are user names.
- TLS. Dashies does not speak Oracle native network encryption, so a
  self-managed instance needs a TCPS listener and an Amazon RDS instance needs
  the SSL option rather than the native-encryption one.
- On Autonomous Database, the **instance** wallet and its password.

Full page: [Connect Oracle Database](/guides/connect-warehouse/oracle).
:::

::::

## Where to add one

You add a data source in the Dashies web app, never through your AI tool.
Credentials are entered in the app's own form and are never passed through the AI
or the publish service, which is why your AI cannot connect a warehouse for you.

Open [dashies.ai/app/connections](https://dashies.ai/app/connections), or use
the account menu, then **Data sources**.

Every form has an optional **Display name** field, hinted `Optional. Helps you
tell data sources apart.` and capped at 120 characters. Set it if you will have
more than one.

## Two connect shapes

Which one you get depends on the engine, and it changes what you do after
clicking Connect.

| Shape | Engines | What happens |
|---|---|---|
| Connect, then Test | PostgreSQL, SQL Server | Creating the data source proves Dashies can reach the warehouse and leaves it `pending`. You then click **Test** to run a real query and move it to `active`. |
| Connect only | BigQuery, Snowflake, Amazon Redshift, Databricks, Oracle Database | Creating the data source runs the verification in the same round trip and comes back already `active`, with the databases or datasets it found. **Oracle Database has a third outcome**: where the verification cannot get an answer in time the data source is saved and reads **Still testing**, and it may settle on its own. It also lists nothing back, by design. |

## Limits that apply to every engine

- **Ten warehouse data sources per workspace.** The eleventh is refused with
  `You have reached the maximum number of warehouse data sources.` Each workspace
  counts separately, so a free slot in one is not a free slot in another.
  **Every data source that is not disabled counts, `pending` ones included** - the
  cap is not a count of the `active` status this page names above, so ten that
  never passed Test still fill the workspace.
- **Twenty connection changes per minute.** Past that you get
  `Too many connection changes. Wait about a minute and try again.`
- **Credentials go into Supabase Vault.** The stored row keeps a pointer to the
  encrypted secret plus non-secret settings such as the host and database name.
  The secret value never appears in the row, an API response, a log line, or an
  audit entry.
- **Failures never echo your host, login, or driver text.** Every failure is
  recorded as a short fixed token such as `connection_auth` or `schema_drift`,
  chosen from a closed set. The set of tokens grows as new kinds of failure get
  their own name, so this page does not list it; the run history names the token
  it recorded, and
  [Refresh failure classes](/reference/errors#refresh-failure-classes) explains
  the ones about your warehouse. Two tokens are about OUR side rather than
  yours and are not on that page yet: `extractor_host` means the machine that
  runs your extract had a problem, and `audit_refused` means our own checks
  declined to publish a refreshed copy. Neither is a fault in your data source,
  and on both your dashboard keeps serving its last good numbers.

:::danger{title="A data source belongs permanently to the scope it was created in"}
A data source is owned by the pair of you and the workspace you created it in,
and neither half can be changed afterwards. There is no move path, and **every
data source belongs to a workspace** - there is no personal scope to create one
in.

The consequence catches people out: a dashboard cannot use a data source from a
**different workspace**, even though your AI can see that data source and its id
is real. Full explanation:
[the workspace trap](/concepts/connections-and-scope#the-workspace-trap). To use a warehouse on a team dashboard, create it from inside that
workspace.

Worse, on a spec publish this does not stop the publish. The dashboard goes live
at a real URL and only the refresh manifest is refused, leaving a dashboard that
can never update. See [Work as a team](/guides/work-as-a-team).
:::

## Two status fields, and why both exist

Each data source shows two independent facts. They disagree normally, and that is
not a bug.

- **Status** is what an explicit Test concluded: `pending`, `active`, or `error`.
  That test may be days old.
- **Health** is what the refresh cron or an authoring call last actually
  observed: `ok` or `failing`.

`active` plus `failing` is a normal reading. It means the last Test passed but a
real query since then did not, which usually means a credential expired or was
rotated. Health gates nothing.

## Check it worked

1. The data source's status reads **active** on
   [dashies.ai/app/connections](https://dashies.ai/app/connections).
2. Ask your AI tool to list connections and introspect the schema:

   > List my Dashies connections, then introspect the schema of the warehouse
   > one.

   You should get back the tables and columns of the schemas, datasets, or
   catalogs you allowlisted. An empty schema list means the login can reach the
   server but cannot see your tables, so re-check the grants on that page's
   setup script.

3. Then [author a dashboard against it](/guides/author-a-dashboard).
