Skip to main content
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.
Use pgcat for server-side pooling when running multiple Permify replicas or when your Postgres connection budget is tight.

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)

  1. Create a pgcat.toml (see “Reference pgcat configuration” below).
  2. Run:

Kubernetes (standalone service)

  1. Create a ConfigMap from your pgcat.toml:
  1. Deploy pgcat:
  1. 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
Notes:
  • Keep Permify max_connection_idle_time lower 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.
Caution (session mode):
  • 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/users fits Postgres max_connections with 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
Debug patterns
  • “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
Churn without pgcat
  • 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)