Skip to main content
Persistence

PostgreSQL

Configure PostgreSQL for self-hosted Rivet deployments.

PostgreSQL is the recommended backend for multi-node self-hosted deployments. It is production-ready for light-to-moderate workloads, up to roughly 1,000 concurrent actors, but is not built for enterprise scale beyond that. For a single-node deployment, use the file system backend (RocksDB-based). Teams running larger or high-throughput realtime workloads should contact enterprise support about FoundationDB.

Overview

PostgreSQL is the storage and coordination backend for self-hosted Rivet deployments. It can run a single engine node on its own, or back multiple engine nodes when paired with NATS. Rivet handles leader election, failover, and version sequencing internally.

Use PostgreSQL when you need:

  • A durable, managed system of record instead of local RocksDB storage.
  • Multiple engine nodes behind a load balancer for redundancy and horizontal scaling (requires NATS).
  • Multi-region deployments (deploy one PostgreSQL instance per region, see Multi-Region).
  • High availability with a managed or self-managed primary/replica failover setup.

Choosing a Backend

Pick your database backend based on how many engine nodes you run:

  • Single-node: Use the file system backend (RocksDB). It is the recommended production-ready backend for single-node deployments. RocksDB is local to one node and cannot be shared across engine instances, and no pub/sub is needed.
  • Multi-node: Use PostgreSQL as the database and NATS for pub/sub. PostgreSQL can be shared across engine nodes and is production-ready for light-to-moderate workloads, up to roughly 1,000 concurrent actors.
  • Enterprise scale: Beyond that, or for high-throughput realtime workloads, PostgreSQL is not the right fit. Contact enterprise support about FoundationDB.

Single-Node vs Multi-Node

Whether PostgreSQL runs Rivet single-node or multi-node depends on whether NATS is configured for pub/sub:

  • Single-node (no NATS): Configure only postgres. Pub/sub uses the in-memory driver and UniversalDB runs in single-node mode with an in-process commit resolver. Run exactly one engine node against this deployment.
  • Multi-node (with NATS): Configure postgres and a top-level nats block for pub/sub. Pub/sub runs over NATS, and UniversalDB inherits that NATS config to run in multi-node mode, using NATS for follower-to-leader commit transport. Multiple engine nodes can then share the same PostgreSQL instance.

Configure NATS for pub/sub to make PostgreSQL multi-node. PostgreSQL inherits the pub/sub NATS config automatically, so a single top-level nats block drives both pub/sub and UniversalDB multi-node coordination. See Multi-Node Configuration below.

Do not run more than one engine node against a PostgreSQL deployment without NATS configured. Without NATS, UniversalDB runs single-node: the first node takes leadership, and any additional node cannot obtain leadership and will eventually error.

Basic Configuration

A single-node deployment needs only a postgres block:

Multi-Node Configuration

To run multiple engine nodes against the same PostgreSQL instance, add a top-level nats block to configure NATS for pub/sub. PostgreSQL inherits this config to run multi-node. Point every engine node at the same PostgreSQL instance and the same NATS cluster:

The top-level nats block configures pub/sub, and UniversalDB inherits it to enable multi-node mode. This is the recommended way to make PostgreSQL multi-node. For NATS high availability, run at least two NATS replicas.

postgres.nats is for advanced setups only: set it to point UniversalDB at a different NATS cluster than the one used for pub/sub. Leave it unset and rely on inheritance from the top-level nats block unless you specifically need to separate the two.

Requirements and Recommendations

Version

Use PostgreSQL 14 or newer. Rivet is tested against PostgreSQL 18, which is recommended for new deployments.

Connection Limits

Each Rivet engine node opens a pool of direct connections to PostgreSQL and can use well over a hundred connections per node under load. PostgreSQL’s default max_connections of 100 is too low for even a single busy engine node.

  • Set PostgreSQL max_connections to comfortably exceed (number of engine nodes × 150) plus headroom for backups, monitoring, and your own queries.
  • If you use a managed PostgreSQL service, confirm its connection limit is high enough or pick a tier that allows raising it. Connection exhaustion shows up as engine startup failures or stalled requests under load.

Do not work around the connection limit with a connection pooler. See Do Not Use Connection Poolers below.

Resources

PostgreSQL is the system of record for the entire deployment, so size it accordingly:

  • Give PostgreSQL dedicated CPU, memory, and fast disk (SSD/NVMe with high IOPS). Avoid co-locating it with other heavy workloads.
  • Rivet generates steady write and row-turnover on its internal tables. Keep autovacuum enabled and healthy so dead tuples do not accumulate.

High Availability and Backups

A single PostgreSQL instance is a single point of failure for your whole deployment.

  • Configure a standby replica with automatic failover (managed services such as Amazon RDS, Cloud SQL, and Azure Database provide this).
  • Enable automated backups and point-in-time recovery, and periodically test restoring from them.

Multi-Region

Deploy one PostgreSQL instance per region or datacenter. Engine nodes connect to the PostgreSQL instance in their own region. See Multi-Region for the full topology.

Managed Postgres Compatibility

Some hosted PostgreSQL platforms require additional configuration due to platform-specific restrictions.

SSL/TLS Support

To enable SSL for Postgres, add sslmode=require to your PostgreSQL connection URL:

The sslmode parameter controls TLS usage:

  • disable: Do not use TLS
  • prefer: Use TLS if available, otherwise connect without TLS (default)
  • require: Require TLS connection (fails if TLS is not available)

To verify the server certificate against a CA or verify the hostname, use custom SSL certificates (see below).

Custom SSL Certificates

For databases using custom certificate authorities (e.g., Supabase) or requiring client certificate authentication, you can specify certificate paths in the configuration:

ParameterDescriptionPostgreSQL Equivalent
root_cert_pathPath to the root certificate file for verifying the server’s certificatesslrootcert
client_cert_pathPath to the client certificate file for client certificate authenticationsslcert
client_key_pathPath to the client private key file for client certificate authenticationsslkey

All SSL paths are optional. If not specified, Rivet uses the default system root certificates from Mozilla’s root certificate store.

Do Not Use Connection Poolers

Rivet requires direct PostgreSQL connections for session-level features and does not support connection poolers.

Do not use:

  • PgBouncer
  • Supavisor
  • AWS RDS Proxy

Troubleshooting

Too Many Connections

Errors like FATAL: sorry, too many clients already or engine nodes failing to start under load mean PostgreSQL’s max_connections is too low. Raise it to account for every engine node (see Connection Limits). Do not add a connection pooler to work around this.

Connection Refused or TLS Errors

  • Confirm the engine connects directly to PostgreSQL and not through a pooler (PgBouncer, Supavisor, RDS Proxy). Rivet requires direct connections.
  • For TLS errors, verify sslmode matches your server and, for custom certificate authorities, that ssl.root_cert_path points to the correct CA certificate. See SSL/TLS Support.