Skip to main content
Dreamlit connects to your database the same way analytics and ETL tools you probably already run do, such as Airbyte, Metabase, or Fivetran: a read-only user that reads your tables and follows the change feed. Nothing is installed in your database, and nothing is ever written to it. This page covers exactly what that connection can and can’t do, and what it costs your database.

Connecting, at a glance

You can connect any PostgreSQL database, including Supabase, Neon, RDS, and Cloud SQL. Dreamlit uses the connection for two things: building and diagnosing your user funnel, and hooking into key events in real time to kick off email workflows, with no code for your engineering team to instrument. It is best practice to create a separate database user for each service that connects to your database, following security principles of least privilege and isolation. That way you can see exactly what each one is allowed to do, and revoke any of them independently. To create one for Dreamlit, you can run the following SQL, replacing <SECRET_PASSWORD> with a strong, unique password:
You’ll notice the role has two main permissions attached to it: reading data, and replication.

Read-only access

The dreamlit_readonly user has read access and nothing else. It can’t insert, update, delete, or alter anything, and it can’t create databases or other users. No changes are ever made to your data. Read access is what lets Dreamlit build your funnel and render email previews with real rows. By default it’s granted through pg_read_all_data, PostgreSQL’s built-in read-only role, which covers tables you add later without touching the grant again. If you’d rather limit it further, grant SELECT on specific tables instead:

Replication

Replication access is what lets a workflow start the moment something happens, instead of on a polling schedule. It works through PostgreSQL logical replication using wal2json, the same mechanism your database uses for its own replicas. By default, nothing is replicated. Connecting the user creates no replication slot and follows no tables. Only when a workflow goes live does Dreamlit subscribe to that workflow’s trigger table. Tables with no live workflow are never streamed. No data is retained. The change feed is not copied anywhere. Each change is evaluated against the live workflows and discarded. The only thing that persists is the state a running workflow needs to finish, such as which user it’s emailing and which step it’s on, and that lives only as long as the run. It’s robust to outages. If the connection drops, or Dreamlit is briefly unavailable, your database queues the changes in the slot until we reconnect, then Dreamlit picks up exactly where it left off. Nothing is missed, and nobody gets the same email twice. The queue is bounded by your max_slot_wal_keep_size setting, so it can’t grow without limit. To see what Dreamlit is currently subscribed to, run this on your database:
No rows means nothing is being replicated. One row per connection means a workflow is live; the tables it follows are listed on that connection in your Dreamlit dashboard, and the active column shows whether Dreamlit is connected right now.

Performance

Following the change feed is light work for your database, much lighter than polling your tables. Your database decodes only the tables Dreamlit is subscribed to, and the read-only queries for funnel analysis run in small batches in the background. If you’d rather keep Dreamlit off your primary entirely, point it at a read replica. Logical replication from a replica requires PostgreSQL 16 or newer; on older versions, the connection needs the primary for the change feed and can use a replica for everything else.