# Supabase Postgres CDC (/docs/integrations/supabase-postgres-cdc)





Supabase Postgres CDC [#supabase-postgres-cdc]

This example streams a Supabase Postgres table into RawTree with Change Data Capture.

<img alt="Superstore Sales Analytics via Supabase CDC" src="__img0" /> This example imports the Kaggle Superstore Sales dataset into Supabase.
It publishes the table through logical replication.
A [`supabase/etl`](https://github.com/supabase/etl) worker sends the initial copy and later inserts, updates, and deletes to an append-only RawTree table.

The walkthrough uses the Kaggle Superstore Sales dataset as sample data. Use the [Showroom version](https://showroom.rawtree.com/use-cases/supabase-superstore-cdc) for the curated dashboard and live walkthrough.

What this demonstrates [#what-this-demonstrates]

* Initial copy plus live CDC from Supabase Postgres.
* Append-only event storage in RawTree.
* Terraform-managed deployment to ECS Fargate.
* A build-from-source path for understanding and debugging the worker.
* Dashboard queries that reconstruct the latest table state from CDC events.

1\. Prepare the source table in Supabase [#1-prepare-the-source-table-in-supabase]

Create a Supabase project.
Import the Superstore Sales CSV through **Table Editor → New table → Import data from CSV**.
Name the table `superstore_sales_data` in the `public` schema.

Supabase preserves the CSV column names verbatim, including spaces such as `Row ID` and `Order ID`.

Enable logical replication for the table:

```sql
-- REPLICA IDENTITY FULL makes UPDATEs and DELETEs include the full old row,
-- which the worker needs to emit complete CDC events.
ALTER TABLE public.superstore_sales_data REPLICA IDENTITY FULL;

DROP PUBLICATION IF EXISTS rawtree_superstore_publication;
CREATE PUBLICATION rawtree_superstore_publication
FOR TABLE public.superstore_sales_data;
```

2\. Get the connection URL and CA certificate [#2-get-the-connection-url-and-ca-certificate]

Copy the **Direct connection** string from **Project Settings → Database → Connection string**. Do not use the pooler URL; logical replication requires a direct replication connection.

Download the Supabase CA certificate from **Project Settings → Database → SSL Configuration**.

You can also dump the CA from the live TLS handshake:

```bash
host=db.<your-project-ref>.supabase.co
openssl s_client -showcerts -starttls postgres -connect "$host:5432" </dev/null 2>/dev/null \
  | awk '/-----BEGIN CERT/,/-----END CERT/' > ~/supabase-ca.crt
```

Supabase direct endpoints are commonly IPv6-only. If you deploy to ECS, use a dual-stack subnet so the worker can reach Supabase.

3\. Configure credentials [#3-configure-credentials]

Set RawTree credentials as environment variables. The AWS provider uses your default AWS credentials or `AWS_PROFILE`.

```bash
export RAWTREE_API_KEY="rt_..."
export RAWTREE_ORG="your-org"
export RAWTREE_DATABASE="your-database"

export TF_VAR_supabase_database_url='postgres://postgres:PASS@db.<ref>.supabase.co:5432/postgres?sslmode=require'
```

supabase/etl [#supabaseetl]

[`supabase/etl`](https://github.com/supabase/etl) is Supabase's Rust framework for Postgres logical-replication pipelines.
It handles the initial copy and streams ongoing inserts, updates, and deletes.
Destinations can write those events to their chosen targets.
See the [supabase/etl documentation](https://supabase.github.io/etl/) for project details.

4\. Deploy with Terraform [#4-deploy-with-terraform]

A single `rawtree_supabase_cdc_ingestion` resource provisions the worker infrastructure: Secrets Manager, IAM, CloudWatch Logs, ECS cluster, and a long-running Fargate service.

```hcl
terraform {
  required_providers {
    rawtree = { source = "rawtreedb/rawtree" }
    aws     = { source = "hashicorp/aws", version = "~> 5.0" }
  }
}

provider "rawtree" {}
provider "aws" { region = "us-east-1" }

variable "supabase_database_url" {
  type      = string
  sensitive = true
}

resource "rawtree_supabase_cdc_ingestion" "superstore" {
  name        = "superstore"
  region      = "us-east-1"
  publication = "rawtree_superstore_publication"

  # For production, prefer database_url_secret_arn and
  # tls_root_cert_secret_arn pointing at secrets you manage.
  # Those values never enter Terraform state.
  database_url      = var.supabase_database_url
  tls_root_cert_pem = file("~/supabase-ca.crt")

  subnet_ids       = [aws_subnet.this.id] # dual-stack subnet
  assign_public_ip = true                 # IPv4 for the ghcr.io image pull

  cpu    = 512
  memory = 1024
}

output "log_group_name" {
  value = rawtree_supabase_cdc_ingestion.superstore.log_group_name
}
```

5\. Apply and verify [#5-apply-and-verify]

Apply the configuration.
Tail the worker logs.
On first start, the worker copies existing Superstore rows into `public_superstore__sales__data`.
It then streams live changes from the replication slot.

```bash
terraform init
terraform apply

aws logs tail $(terraform output -raw log_group_name) --follow

rtree query "SELECT count() FROM public_superstore__sales__data"
```

6\. Open the dashboard [#6-open-the-dashboard]

Create a read-only API key for the dashboard:

```bash
rtree key create --name superstore-dashboard --permission read
```

Then open the [Showroom dashboard](https://showroom.rawtree.com/use-cases/supabase-superstore-cdc/dashboard). Insert, update, or delete rows in Supabase and watch the CDC operations update within seconds.

Related [#related]

* [supabase/etl repository](https://github.com/supabase/etl)
* [supabase/etl documentation](https://supabase.github.io/etl/)
* [Query data](/docs/guides/query/query-data)
* [Superstore Showroom page](https://showroom.rawtree.com/use-cases/supabase-superstore-cdc)
