SpiceDB Documentation
Concepts
Postgresql

PostgreSQL

Usage Notes

  • Recommended for single-region deployments
  • Postgres 15 or newer is required for optimal performance
  • Resiliency to failures only when PostgreSQL is operating with a follower and proper failover
  • Carries setup and operational complexity of running PostgreSQL
  • Does not rely on any non-standard PostgreSQL extensions
  • Compatible with managed PostgreSQL services (e.g. AWS RDS)
  • Can be scaled out on read workloads using read replicas
⚠️

SpiceDB's Watch API requires PostgreSQL's Commit Timestamp tracking (opens in a new tab) to be enabled.

This can be done by providing the --track_commit_timestamp=on flag, configuring postgresql.conf, or executing ALTER SYSTEM SET track_commit_timestamp = on; and restarting the instance.

Developer Notes

Read Replicas

SpiceDB supports Postgres read replicas and does it while retaining consistency guarantees. Typical use cases are:

  • scale read workloads/offload reads from the primary
  • deploy SpiceDB in other regions with primarily read workloads

Read replicas are typically configured with asynchronous replication, which involves replication lag. That would be problematic to SpiceDB's ability to solve the new enemy problem but it addresses the challenge by checking if a revision has been replicated into the target replica. If missing, it will fall back to the primary.

All API consistency options will leverage replicas, but the ones that benefit the most are those that involve some level of staleness as it increases the odds a revision has replicated. minimize_latency, at_least_as_fresh, and at_exact_snapshot consistency modes have the highest chance of being redirected to a replica.

SpiceDB supports Postgres replicas behind a load-balancer, and/or individually listing replica hosts. When multiple URIs are provided, they will be queried using a round-robin strategy.
Please note that the maximum number of replica URIs to list is 16.

Read replicas are configured with the --datastore-read-replica-* family of flags.

SpiceDB supports PgBouncer (opens in a new tab) connection pooler and is part of the test suite.

Transaction IDs and MVCC

The Postgres implementation of SpiceDB's internal MVCC mechanism involves storing the internal transaction ID count associated with a given transaction in the rows written in that transaction. Because this counter is instance-specific, there are ways in which the data in the datastore can become desynced with that internal counter. Two concrete examples are the use of pg_dump and pg_restore to transfer data between an old instance and a new instance and setting up logical replication between a previously-existing instance and a newly-created instance.

If you encounter this, SpiceDB can behave as though there is no schema written, because the data (including the schema) is associated with a future transaction ID and therefore isn't "visible" to Spicedb. If you run into this issue, the fix is documented here (opens in a new tab)

Configuration

Required Parameters

ParameterDescriptionExample
datastore-enginethe datastore engine--datastore-engine=postgres
datastore-conn-uriconnection string used to connect to PostgreSQL--datastore-conn-uri="postgres://postgres:password@localhost:5432/spicedb?sslmode=disable"

Optional Parameters

ParameterDescriptionExample
datastore-conn-pool-read-max-idletimeMaximum amount of time a connection can idle in a remote datastore's connection pool (default 30m0s)--datastore-conn-pool-read-max-idletime=30m0s
datastore-conn-pool-read-max-lifetimeMaximum amount of time a connection can live in a remote datastore's connection pool (default 30m0s)--datastore-conn-pool-read-max-lifetime=30m0s
datastore-conn-pool-read-max-lifetime-jitterWaits rand(0, jitter) after a connection is open for max lifetime to actually close the connection--datastore-conn-pool-read-max-lifetime-jitter=6m
datastore-conn-pool-read-max-openNumber of concurrent connections open in a remote datastore's connection pool (default 20)--datastore-conn-pool-read-max-open=20
datastore-conn-pool-read-min-openNumber of minimum concurrent connections open in a remote datastore's connection pool (default 20)--datastore-conn-pool-read-min-open=20
datastore-conn-pool-write-healthcheck-intervalAmount of time between connection health checks in a remote datastore's connection pool (default 30s)--datastore-conn-pool-write-healthcheck-interval=30s
datastore-conn-pool-write-max-idletimeMaximum amount of time a connection can idle in a remote datastore's connection pool (default 30m0s)--datastore-conn-pool-write-max-idletime=30m0s
datastore-conn-pool-write-max-lifetimeMaximum amount of time a connection can live in a remote datastore's connection pool (default 30m0s)--datastore-conn-pool-write-max-lifetime=30m0s
datastore-conn-pool-write-max-lifetime-jitterWaits rand(0, jitter) after a connection is open for max lifetime to actually close the connection--datastore-conn-pool-write-max-lifetime-jitter=6m
datastore-conn-pool-write-max-openNumber of concurrent connections open in a remote datastore's connection pool (default 10)--datastore-conn-pool-write-max-open=10
datastore-conn-pool-write-min-openNumber of minimum concurrent connections open in a remote datastore's connection pool (default 10)--datastore-conn-pool-write-min-open=10
datastore-query-split-sizeThe (estimated) query size at which to split a query into multiple queries--datastore-query-split-size=5kb
datastore-gc-windowSets the window outside of which overwritten relationships are no longer accessible--datastore-gc-window=1s
datastore-revision-fuzzing-durationSets a fuzzing window on all zookies/zedtokens--datastore-revision-fuzzing-duration=50ms
datastore-readonlyPlaces the datastore into readonly mode--datastore-readonly=true
datastore-read-replica-conn-uriConnection string used by datastores for read replicas; only supported for postgres and MySQL--datastore-read-replica-conn-uri="postgres://postgres:password@localhost:5432/spicedb\"
datastore-read-replica-credentials-provider-nameRetrieve datastore credentials dynamically using aws-iam
datastore-read-replica-conn-pool-read-healthcheck-intervalamount of time between connection health checks in a read-only replica datastore's connection pool--datastore-read-replica-conn-pool-read-healthcheck-interval=30s
datastore-read-replica-conn-pool-read-max-idletimemaximum amount of time a connection can idle in a read-only replica datastore's connection pool--datastore-read-replica-conn-pool-read-max-idletime=30m
datastore-read-replica-conn-pool-read-max-lifetimemaximum amount of time a connection can live in a read-only replica datastore's connection pool--datastore-read-replica-conn-pool-read-max-lifetime=30m
datastore-read-replica-conn-pool-read-max-lifetime-jitterwaits rand(0, jitter) after a connection is open for max lifetime to actually close the connection to a read replica(default: 20% of max lifetime)--datastore-read-replica-conn-pool-read-max-lifetime-jitter=6m
datastore-read-replica-conn-pool-read-max-opennumber of concurrent connections open in a read-only replica datastore's connection pool--datastore-read-replica-conn-pool-read-max-open=20
datastore-read-replica-conn-pool-read-min-opennumber of minimum concurrent connections open in a read-only replica datastore's connection pool--datastore-read-replica-conn-pool-read-min-open=20
© 2025 AuthZed.