Pgcat is a PostgreSQL pooler with sharding, load balancing, and failover support. It provides server-side connection pooling, allowing multiple Permify replicas to share a centralized connection pool, reducing the total number of connections to your PostgreSQL database.For more information, see the pgcat repository.
When to use pgcat vs direct Postgres
- pgcat (recommended): Centralized pooling/multiplexing across all Permify pods. Easier scaling, fewer server connections.
- Direct Postgres: Use a small warm client pool in Permify to avoid cold dials during bursts.
Installation
Docker (quick start)
- Create a pgcat.toml (see “Reference pgcat configuration” below).
- Run:
Kubernetes (standalone service)
- Create a ConfigMap from your pgcat.toml:
- Deploy pgcat:
- Update network policies/security groups so Permify pods can reach the pgcat Service.
Kubernetes (sidecar pattern)
Run pgcat as a sidecar next to Permify in the same Pod for very small deployments. In most cases a shared pgcat Service is simpler to operate and scale.Reference pgcat configuration (session mode)
File: pgcat.toml- Keep Permify
max_connection_idle_timelower than pgcat’s idle/server timeouts so the client drops first.
Permify → Pgcat configuration
Keep the client pool small; pgcat owns the warm pool.Query parameters for session mode:
-
plan_cache_mode=force_custom_plan: Forces PostgreSQL to create custom plans for each execution instead of reusing generic plans. Prevents plan cache conflicts when pgcat reuses backend connections across different client sessions. See PostgreSQL documentation. -
default_query_exec_mode=cache_describe: Configures pgx to cache only the Describe phase without creating server-side named prepared statements. Avoids prepared statement conflicts when pgcat reuses backend connections, while still optimizing the Describe round-trip.
- Non-zero min_connections across many pods can pin backend sessions and exhaust server connection budget.
Direct Postgres (no proxy)
Use a small warm client pool to reduce cold connects.Sizing rules of thumb
-
With pgcat
- min_connections: 0
- max_connections: 1 per pod (raise to 2–5 only if you observe client-side waits)
- Ensure
pool_size x shards/usersfits Postgresmax_connectionswith headroom
-
Direct Postgres
- min_connections: 1–3, max_connections sized to real concurrency/CPU
- Keep idle/lifetime settings reasonable to avoid thundering-herd reconnects
Monitoring
Track both sides and correlate spikes:- pgcat: pool utilization, server connection counts, read/write routing, query latency
- Permify: pool (in-use/total), “connect” spans, error rates
- “Connect” spikes + rising pgcat server connections → proxy opening backends (cold path)
- “Connect” spikes + Idle=0 in client pool → client pool exhaustion (no warm conns)
Troubleshooting
Connect spikes with pgcat- Keep min_connections: 0
- Ensure max_connection_idle_time < pgcat idle/server timeout
- In session mode with many pods, check pool_size and server connection headroom
- Set min_connections: 1–3
Backward compatibility
- max_open_connections → deprecated, use max_connections instead (still works)
- max_idle_connections → deprecated, use min_connections instead (still works, maps to min_connections if not set)