Connecting to Postgres

In the future, Zero will work with many different backend databases. Today only Postgres is supported. Specifically, Zero requires Postgres v15.0 or higher, and support for logical replication.

Here are some common Postgres options and what we know about their support level:

PostgresSupport Status
AWS RDS
AWS Aurora✅  v15.6+
Google Cloud SQL✅  See notes below
Fly.io Managed Postgres (MPG)⚠️  Private network only; no event triggers; see notes below
Neon✅  See notes below
PlanetScale for Postgres✅  See notes below
Postgres.app
postgres:16.2-alpine docker image
Supabase⚠️  See notes below
Render⚠️  See notes below
Heroku🤷‍♂️  No 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).
  • Provider docs for pooling/proxies: Neon, PlanetScale, Supabase, AWS RDS Proxy, Fly MPG, Render, Cloud SQL.
  • 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) and set App Publications.

Event Triggers

Zero uses Postgres “Event Triggers” when possible to implement high-quality, efficient schema migration.

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

WAL Level

The Postgres wal_level config parameter has to be set to logical. You can check what level your pg has with this command:

psql -c 'SHOW wal_level'

If it doesn’t output logical then you need to change the wal level. To do this, run:

psql -c "ALTER SYSTEM SET wal_level = 'logical';"

Then restart Postgres. On most pg systems you can do this like so:

data_dir=$(psql -t -A -c 'SHOW data_directory')
pg_ctl -D "$data_dir" restart

After your server restarts, show the wal_level again to ensure it has changed:

psql -c 'SHOW wal_level'

Bounding WAL Size

For development databases, you can set a max_slot_wal_keep_size value in Postgres. This will help limit the amount of WAL kept around.

This is a configuration parameter that bounds the amount of WAL kept around for replication slots, and invalidates the slots that are too far behind.

zero-cache will automatically detect if the replication slot has been invalidated and re-sync replicas from scratch.

This configuration can cause problems like slot has been invalidated because it exceeded the maximum reserved size and is not recommended for production databases.

Provider-Specific Notes

Google Cloud SQL

Zero works with Google Cloud SQL out of the box. In many configurations, when you connect with a user that has sufficient privileges, zero-cache will create its default publication automatically.

If your Cloud SQL user does not have permission to create publications, you can still use Zero by creating a publication manually and then specifying that publication name in App Publications when running zero-cache.

On Google Cloud SQL for PostgreSQL, enable logical decoding by turning on the instance flag cloudsql.logical_decoding. You do not set wal_level directly on Cloud SQL. See Google's documentation for details: Configure logical replication.

Fly.io

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 (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.

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.

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

Postgres Version

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. See Supabase's docs for upgrading your Postgres version.

Connection Type (Direct vs Pooler)

ZERO_UPSTREAM_DB must use the "Direct Connection" string (not the pooler):

Use the "Direct Connection" option for ZERO_UPSTREAM_DB.

Use the "Direct Connection" option for ZERO_UPSTREAM_DB.

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:

Assign an IPv4 address if you have trouble connecting from residential internet.

Assign an IPv4 address if you have trouble connecting from residential internet.

This will be required if you cannot use IPv6 from wherever zero-cache is running. Most cloud providers support IPv6, but some do not. For example, if you are running zero-cache in AWS, it is possible to use IPv6 but difficult. Hetzner offers cheap hosted VPS that supports IPv6.

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, 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.

PlanetScale for Postgres

  • 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.

For production databases that have enough usage to always be running anyway, this is fine. But for smaller applications that would otherwise not always be running, this can create a surprisingly high bill. You may want to choose a provider that charge a flat monthly rate instead.

Also some users choose Neon because they hope to use branching for previews. This can work, but if not done with care, Zero can end up keeping each Neon preview branch running too 😳.

For the recommended approach to preview URLs, see Preview Deployments.