diff --git a/contents/docs/connecting-to-postgres.mdx b/contents/docs/connecting-to-postgres.mdx
index f5530881..120a76db 100644
--- a/contents/docs/connecting-to-postgres.mdx
+++ b/contents/docs/connecting-to-postgres.mdx
@@ -6,19 +6,27 @@ In the future, Zero will work with many different backend databases. Today only
Here are some common Postgres options and what we know about their support level:
-| Postgres | Support Status |
-| --------------------------------- | ---------------------------------------------------------- |
-| AWS RDS | ✅ |
-| AWS Aurora | ✅ v15.6+ |
-| Google Cloud SQL | ✅ See [notes below](#google-cloud-sql) |
-| [Fly.io](https://fly.io) Postgres | ✅ See [notes below](#flyio) |
-| Neon | ✅ See [notes below](#neon) |
-| PlanetScale for Postgres | ✅ See [notes below](#planetscale-for-postgres) |
-| Postgres.app | ✅ |
-| postgres:16.2-alpine docker image | ✅ |
-| Supabase | ✅ See [notes below](#supabase) |
-| Render | 🤷♂️ No [event triggers](#event-triggers) |
-| Heroku | 🤷♂️ No [event triggers](#event-triggers) |
+| Postgres | Support Status |
+| ----------------------------------------------- | --------------------------------------------------------------------------------------------------- |
+| AWS RDS | ✅ |
+| AWS Aurora | ✅ v15.6+ |
+| Google Cloud SQL | ✅ See [notes below](#google-cloud-sql) |
+| [Fly.io](https://fly.io) Managed Postgres (MPG) | ⚠️ Private network only; no [event triggers](#event-triggers); see [notes below](#flyio) |
+| Neon | ✅ See [notes below](#neon) |
+| PlanetScale for Postgres | ✅ See [notes below](#planetscale-for-postgres) |
+| Postgres.app | ✅ |
+| postgres:16.2-alpine docker image | ✅ |
+| Supabase | ⚠️ See [notes below](#supabase) |
+| Render | ⚠️ See [notes below](#render) |
+| Heroku | 🤷♂️ No [event triggers](#event-triggers) |
+
+## Common Problems
+
+- `too many connections` / `remaining connection slots are reserved`: use pooled URLs for `ZERO_CVR_DB` and `ZERO_CHANGE_DB`, and tune `ZERO_CVR_MAX_CONNS`, `ZERO_CHANGE_MAX_CONNS`, and `ZERO_UPSTREAM_MAX_CONNS` (see [zero-cache Config](/docs/zero-cache-config)).
+- Provider docs for pooling/proxies: [Neon](https://neon.tech/docs/connect/connection-pooling), [PlanetScale](https://planetscale.com/docs/postgres/connecting/pgbouncer), [Supabase](https://supabase.com/docs/guides/database/connecting-to-postgres), [AWS RDS Proxy](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/rds-proxy.html), [Fly MPG](https://fly.io/docs/mpg/create-and-connect/), [Render](https://render.com/docs/postgresql), [Cloud SQL](https://docs.cloud.google.com/sql/docs/postgres/managed-connection-pooling).
+- `prepared statement ... does not exist`: your pooler is likely in transaction mode; use a session pooler (or a pooler that supports prepared statements) for `ZERO_CVR_DB` and `ZERO_CHANGE_DB`.
+- `permission denied to create event trigger`: your provider/role doesn’t grant superuser; Zero will fall back to full resets on schema changes.
+- `permission denied` creating a publication for a schema/all tables: create a publication listing tables explicitly (see [Limiting Replication](/docs/postgres-support#limiting-replication)) and set [App Publications](/docs/zero-cache-config#app-publications).
## Event Triggers
@@ -26,6 +34,8 @@ Zero uses Postgres “[Event Triggers](https://www.postgresql.org/docs/current/s
Some hosted Postgres providers don’t provide access to Event Triggers.
+Some managed providers also have incomplete Event Trigger behavior for certain DDL (for example, `ALTER PUBLICATION`). We call out known provider-specific issues below.
+
Zero still works out of the box with these providers, but for correctness, any schema change triggers a full reset of all server-side and client-side state. For small databases (< 10GB) this can be OK, but for bigger databases we recommend choosing a provider that grants access to Event Triggers.
## Configuration
@@ -81,9 +91,17 @@ See Google's documentation for details: [Configure logical replication](https://
### Fly.io
-Fly does not support TLS on their internal networks. If you run both `zero-cache` and Postgres on Fly, you need
-to stop `zero-cache` from trying to use TLS to talk to Postgres. You can do this by adding the `sslmode=disable`
-query parameter to your connection strings from `zero-cache`.
+Fly's legacy "Fly Postgres" offering is deprecated; the current product is Fly Managed Postgres (MPG).
+
+Fly Managed Postgres is private-network-only by default. If `zero-cache` runs outside Fly, connect via Fly WireGuard or run a proxy like [fly-mpg-proxy](https://github.com/fly-apps/fly-mpg-proxy) (restrict ingress / IP allowlist).
+
+If you use Fly's pgBouncer endpoint for pooled connections (for example, for `ZERO_CVR_DB` / `ZERO_CHANGE_DB`), note that it defaults to **session** pooling; keep it that way. Transaction pooling can break prepared statements.
+
+Fly Managed Postgres does not provide superuser access, so `zero-cache` cannot create [event triggers](#event-triggers).
+
+Also, some publication operations (like `FOR TABLES IN SCHEMA ...` / `FOR ALL TABLES`) can be permission-restricted. If `zero-cache` can’t create its default publication, create one listing tables explicitly and set [App Publications](/docs/zero-cache-config#app-publications).
+
+Fly does not support TLS on its private network. If `zero-cache` connects to Postgres over the Fly private network (including WireGuard), add `sslmode=disable` to your connection strings.
### Supabase
@@ -91,18 +109,22 @@ query parameter to your connection strings from `zero-cache`.
Supabase requires at least 15.8.1.083 for event trigger support. If you have a lower 15.x, Zero will still work but [schema updates will be slower](#event-triggers). See Supabase's docs for upgrading your Postgres version.
-#### Connection Type
+#### Connection Type (Direct vs Pooler)
-In order to connect to Supabase you must use the "Direct Connection" style connection string, not the pooler:
+`ZERO_UPSTREAM_DB` must use the "Direct Connection" string (not the pooler):
This is because Zero sets up a logical replication slot, which is only supported with a direct connection.
+For `ZERO_CVR_DB` and `ZERO_CHANGE_DB`, prefer Supabase’s **session** pooler (not the default **transaction** pooler). The transaction pooler can break prepared statements and cause errors like `26000 prepared statement ... does not exist`.
+
+Known limitation: Supabase does not fire `ddl_command_start`/`ddl_command_end` event triggers for `ALTER PUBLICATION`, so Zero may not automatically detect publication changes.
+
#### IPv4
You may also need to assign an IPv4 address to your Supabase instance:
@@ -120,13 +142,23 @@ difficult. [Hetzner](https://www.hetzner.com/) offers cheap hosted VPS that supp
IPv4 addresses are only supported on the Pro plan and are an extra $4/month.
+### Render
+
+Render can work with Zero, but commonly requires admin/support-side setup:
+
+- Ensure `wal_level=logical` (may require a Render support ticket).
+- App roles typically can’t create [event triggers](#event-triggers), so schema changes will fall back to full resets.
+- App roles may not be able to create schema/all-table publications; create a publication listing tables explicitly and set [App Publications](/docs/zero-cache-config#app-publications).
+
### PlanetScale for Postgres
-* You need to connect using the "default" role that PlanetScale provides, because PlanetScale's "User-Defiend Roles" cannot create replication slots.
-* Be sure to use a direct connection for "Upstream DB" and a pg bouncer connnection string for "CVR DB" and "Change DB". Otherwise you will likely exhaust connection limits to PlanetScale.
+- Use the `default` role that PlanetScale provides, because PlanetScale user-defined roles cannot create replication slots.
+- Use a direct connection for `ZERO_UPSTREAM_DB` and the pgBouncer connection string for `ZERO_CVR_DB` and `ZERO_CHANGE_DB`. Otherwise you will likely exhaust connection limits on PlanetScale.
### Neon
+Neon supports logical replication, but you may need to enable it in the Neon console for your branch/endpoint (otherwise `SHOW wal_level` may return `replica`).
+
Neon fully supports Zero, but you should be aware of how Neon's pricing model and Zero interact.
Because Zero keeps an open connection to Postgres to replicate changes, as long as zero-cache is running, Postgres will be running and you will be charged by Neon.
diff --git a/contents/docs/deployment.mdx b/contents/docs/deployment.mdx
index 12c57d13..2109f818 100644
--- a/contents/docs/deployment.mdx
+++ b/contents/docs/deployment.mdx
@@ -25,6 +25,8 @@ These components have the following characteristics:
You will also need to deploy a Postgres database, your frontend, and your API server for the [query](/docs/queries#server-setup) and [mutate](/docs/mutators#server-setup) endpoints.
+Before choosing/configuring Postgres, read [Connecting to Postgres](/docs/connecting-to-postgres) for provider-specific caveats (direct vs pooled connections, event triggers, connection limits).
+
## Minimum Viable Strategy
The simplest way to deploy Zero is to run everything on a single node. This is the least expensive way to run Zero, and it can take you surprisingly far.
@@ -56,7 +58,7 @@ services:
- 3000:3000
environment:
# Your API handles mutations and writes to the PG db
- # Use a transaction pooler (e.g. pgbouncer) in production
+ # Use a pooler (e.g. pgbouncer) in production
ZERO_UPSTREAM_DB: postgres://postgres:pass@upstream-db:5432/zero
depends_on:
upstream-db:
@@ -71,10 +73,10 @@ services:
# This *must* be a direct connection (not via pgbouncer)
ZERO_UPSTREAM_DB: postgres://postgres:pass@upstream-db:5432/zero
# Used for storing client view records
- # Use a transaction pooler (pgbouncer) in production
+ # Use a pooler (e.g. pgbouncer) in production
ZERO_CVR_DB: postgres://postgres:pass@upstream-db:5432/zero
# Used for storing recent replication log entries
- # Use a transaction pooler in production
+ # Use a pooler in production
ZERO_CHANGE_DB: postgres://postgres:pass@upstream-db:5432/zero
# Path to the SQLite replica
ZERO_REPLICA_FILE: /data/zero.db
@@ -125,7 +127,7 @@ services:
- 3000:3000
environment:
# Your API handles mutations and writes to the PG db
- # Use a transaction pooler (e.g. pgbouncer) in production
+ # Use a pooler (e.g. pgbouncer) in production
ZERO_UPSTREAM_DB: postgres://postgres:pass@upstream-db:5432/zero
depends_on:
upstream-db:
@@ -169,10 +171,10 @@ services:
# This *must* be a direct connection (not via pgbouncer)
ZERO_UPSTREAM_DB: postgres://postgres:pass@upstream-db:5432/zero
# Used for storing client view records
- # Use a transaction pooler (e.g. pgbouncer) in production
+ # Use a pooler (e.g. pgbouncer) in production
ZERO_CVR_DB: postgres://postgres:pass@upstream-db:5432/zero
# Used for storing recent replication log entries
- # Use a transaction pooler in production
+ # Use a pooler in production
ZERO_CHANGE_DB: postgres://postgres:pass@upstream-db:5432/zero
# Path to the SQLite replica
@@ -208,13 +210,13 @@ services:
condition: service_healthy
environment:
# Used for writing to the upstream database
- # Use a transaction pooler in production
+ # Use a pooler in production
ZERO_UPSTREAM_DB: postgres://postgres:pass@upstream-db:5432/zero
# Used for storing client view records
- # Use a transaction pooler in production
+ # Use a pooler in production
ZERO_CVR_DB: postgres://postgres:pass@upstream-db:5432/zero
# Used for storing recent replication log entries
- # Use a transaction pooler in production
+ # Use a pooler in production
ZERO_CHANGE_DB: postgres://postgres:pass@upstream-db:5432/zero
# Path to the SQLite replica
diff --git a/contents/docs/postgres-support.mdx b/contents/docs/postgres-support.mdx
index 6a2a4c8d..1a92a0fb 100644
--- a/contents/docs/postgres-support.mdx
+++ b/contents/docs/postgres-support.mdx
@@ -220,6 +220,15 @@ COMMIT;
Postgres allows you to change published tables/columns with an `ALTER PUBLICATION` statement. Zero automatically adjusts the table schemas on the replica, but it does not receive the pre-existing data.
+
+ Some managed Postgres providers do not fire DDL event
+ triggers for `ALTER PUBLICATION` (we've observed this on
+ Supabase). In that case Zero may not automatically detect
+ publication changes; prefer a provider where these
+ triggers fire reliably, or plan on a reset/resync when
+ changing publications.
+
+
To stream the pre-existing data to Zero, make an innocuous `UPDATE` after adding the tables/columns to the publication:
```sql