---
title: Connect Amazon Redshift
description: Set up the two credentials Redshift needs, an IAM key pair and a Secrets Manager ARN, and control what a Serverless workgroup costs per refresh.
updated: 2026-08-04
tier: pro
engines: [redshift]
---

Amazon Redshift, via the AWS Redshift Data API. Dashies signs each request with
SigV4 and never opens a database socket, so there is no host to enter and nothing
to allowlist.

:::note{title="Newly lit"}
Redshift shipped built against AWS's documented Data API wire shapes rather than
against a Dashies test account, so the first real connections are what confirm
the last details of temporal formatting and error codes. Everything on this page
is supported. If something behaves differently from what you read here, that is
worth reporting rather than working around.
:::

## Redshift needs two separate credentials

This is the part that differs from every other engine, and it is worth being
clear about before you start.

| Credential | What it authenticates | Where it lives |
|---|---|---|
| An IAM access key pair | The API call itself, over SigV4 | Stored encrypted by Dashies |
| A database user and password | The query, inside Redshift | **AWS Secrets Manager**. Only its ARN reaches Dashies |

The database login never leaves AWS. Dashies holds a pointer to it and asks the
Data API to use it.

## Before you start

Do this in AWS:

:::steps

### 1. Have a workgroup or a cluster

Create a **Redshift Serverless** workgroup and namespace, or use a provisioned
cluster. For Serverless, a low base-RPU (minimum capacity) workgroup is the right
starting point. See the cost section below.

### 2. Create a read-only database user

Grant it `usage` on the reporting schema and `select` on its tables, and no write
privileges.

```sql
create user dashies_ro with password 'REPLACE_WITH_A_STRONG_PASSWORD';
grant usage on schema analytics to dashies_ro;
grant select on all tables in schema analytics to dashies_ro;
alter default privileges in schema analytics
  grant select on tables to dashies_ro;
```

### 3. Store that user in Secrets Manager

Create a secret holding that database user and password. Its ARN is what you
paste into Dashies.

### 4. Create an IAM identity for the API calls

Give it a least-privilege policy allowing only the Data API calls Dashies makes,
plus read on that one secret:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DashiesDataApi",
      "Effect": "Allow",
      "Action": [
        "redshift-data:ExecuteStatement",
        "redshift-data:DescribeStatement",
        "redshift-data:GetStatementResult",
        "redshift-data:CancelStatement",
        "redshift-data:ListDatabases"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DashiesReadOneSecret",
      "Effect": "Allow",
      "Action": "secretsmanager:GetSecretValue",
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:dashies-ro-AbCdEf"
    }
  ]
}
```

Replace the secret ARN with your own. Tighten the first statement's `Resource`
from `*` to your workgroup's or cluster's ARN once you have it; the five actions
above are the complete set Dashies uses, so nothing else needs adding.

### 5. Mint its access key pair

You need the access key id and the secret access key.

:::

## Add the data source

Open [dashies.xyz/app/connections](https://dashies.xyz/app/connections), click to
add a data source, and pick **Amazon Redshift**.

| Field | Required | Example | Notes |
|---|---|---|---|
| Region | yes | `us-east-1` | Chosen from a grouped list of 29 commercial regions. |
| Deployment | yes | Serverless | Serverless uses a workgroup; provisioned uses a cluster. |
| Workgroup name | Serverless only | `my-workgroup` | Lower case, digits, hyphens; 3 to 64 characters. |
| Cluster identifier | provisioned only | `my-cluster` | Starts with a letter; up to 63 characters. |
| Database | yes | `dev` | Up to 128 characters. |
| Access key ID | yes | `AKIA...` | `AKIA` followed by 16 upper-case characters or digits. |
| Secret access key | yes | | Exactly 40 characters. |
| Secrets Manager ARN | yes | `arn:aws:secretsmanager:us-east-1:123456789012:secret:dashies-ro-AbCdEf` | |
| Display name | no | `Analytics warehouse` | Up to 120 characters. |

Exactly one of workgroup or cluster must be filled in. If both or neither are,
the form refuses before anything is sent:

```bash
A Redshift data source needs exactly one of a Serverless workgroup or a provisioned cluster.
```

ARNs from the `aws`, `aws-us-gov`, `aws-cn`, and `aws-iso` partitions are all
accepted.

## It verifies in one step

Redshift is a single-step connect. Creating the data source signs one
`ListDatabases` call and comes back **active** with the databases it found, or
**error** with a class. Up to 200 databases are stored. There is no Resync button
on Redshift.

If it fails:

| Message | Meaning |
|---|---|
| `Amazon Redshift denied access. Check the IAM access key and that its policy allows the Redshift Data API and reading the Secrets Manager secret, and that the secret holds the read-only database user and password.` | Any authorization failure across either credential. |
| `We couldn't reach Amazon Redshift. Check the region and try again.` | Network-level failure, usually a wrong region. |
| `We couldn't read that warehouse. Check the database and the workgroup or cluster, then try again.` | The call succeeded but the target was not usable. |

## Cost

:::warning{title="Serverless bills per RPU-second, with a minimum per query"}
Redshift Serverless charges **per RPU-second with a minimum of roughly 60 seconds
per query**, and it auto-pauses when idle. The first query after a pause also
pays a cold resume of roughly 30 to 60 seconds. That resume is why Dashies allows
60 seconds to connect here rather than the 20 it allows elsewhere.

To keep it predictable:

- Use a **low base-RPU (minimum-capacity) Serverless workgroup**.
- Match the refresh cadence to how fast the data moves, rather than refreshing
  hourly by reflex. See [Set a refresh schedule](/guides/refresh-schedule).

A **provisioned cluster** is a flat hourly cost instead, so refresh frequency
does not change the bill.
:::

Connecting is effectively free: the verification is a single `ListDatabases`
metadata call.

## Caps

| Limit | Value | What happens past it |
|---|---|---|
| Rows per dataset query | 100,000 | Refused before results are fetched, by reading Redshift's own reported row count. |
| Data island, whole dashboard | 8,388,608 bytes | Publish is refused. |
| Compiled dashboard body | 5,242,880 bytes | Publish is refused. Usually binds first. |

Like the other three API-based engines, Redshift has **no byte gate at execution
time**: nothing on that path measures serialized size. A wide result with few rows
is therefore caught one layer later, by the island and body limits above, so do
not read the absence of a byte cap as an absence of a ceiling.

Redshift supports every materialization, including Parquet offload for a
row-level dataset.

How these ceilings relate to each other, and which one binds first, is in
[sizes and ceilings](/concepts/dataset-modes#sizes-and-ceilings).

## Dialect notes

Cube SQL is Redshift SQL, which is a PostgreSQL dialect, so almost everything you
know from Postgres applies.

- Bucket a date with `date_trunc('month', ts)::date`, or produce a text bucket
  with `to_char(ts, 'YYYY-MM-DD')`.

  ```sql
  select date_trunc('month', ordered_at)::date as month,
         sum(amount) as revenue
  from analytics.orders
  group by 1
  order by 1
  ```

- Bucket in your business time zone in the SQL. A refresh runs with no session
  time zone, so a bare `date_trunc` buckets in UTC.
- **Alias letter-case depends on two cluster parameters, not on how you write
  it.** At the defaults, quoting an alias preserves nothing, because AWS folds
  delimited identifiers to lower case too. Two settings change that:
  `enable_case_sensitive_identifier = true` makes a quoted identifier keep its
  case, and `describe_field_name_in_uppercase = on` returns every column name in
  upper case regardless. Neither is exotic.

  Rather than guessing which way your cluster is set, ask your AI tool to run one
  validation and read the column names back:

  ```sql
  select 1 as MixedCase, 2 as "MixedQuoted"
  ```

  That answers both parameters in a single call. A case-only difference between
  your alias and your declared measure key is handled for you either way; two
  output columns differing only by case are refused, naming both.

## Rotating the key

Edit the data source and paste a new IAM secret access key under **Secret access
key**. Rotating the database user is done in Secrets Manager, and Dashies picks
it up without any change here, because it holds only the ARN.

## Check it worked

1. The data source reads **active** on
   [dashies.xyz/app/connections](https://dashies.xyz/app/connections), and lists
   the databases it discovered.
2. Ask your AI tool to introspect it and run one query:

   > Introspect my Redshift data source, then validate this cube SQL against it:
   > `select 1 as ok`

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