Hello
I apologize for the category but it seems there is bug when choosing tags and category.
We have multiple Phoenix applications using Fly Managed Postgres (Starter plan), and I’m trying to clearly understand how database connections are counted and how PgBouncer is supposed to help in this setup.
We’re running multiple Phoenix apps, each with its own database, all on the same Postgres cluster. The cluster has a hard limit of 100 connections, which seems to apply to the entire cluster, not per database. This matches what I see when connecting with fly mpg connect and checking pg_stat_activity.
Based on Fly documentation, we configured PgBouncer in transaction mode for runtime traffic and use direct URLs only for migrations and session-sticky operations. Our Ecto repo config looks roughly like this:
config :my_app, MyApp.Repo,
url: System.fetch_env!("DATABASE_URL"),
pool_size: 8,
timeout: 15_000,
prepare: :unnamed
My confusion comes from how the connection limits behave in practice. I originally thought the main purpose of using a connection pooler was to help avoid hitting the Postgres max_connections limit. However, when looking at Fly metrics, I see both “Pooled Connections” and “Database Connections” increasing, and once database connections exceed ~100, the entire cluster stops responding.
In one example, a single app running on two machines uses about 17 connections when using the direct database URL, which is predictable and stays well below the limit. When using PgBouncer, the number of database connections sometimes appears lower, sometimes higher, and overall feels harder to reason about. Across multiple apps and multiple databases, it still seems easy to hit the 100-connection cluster limit.
This makes it look like PgBouncer doesn’t really protect us from the cluster-wide connection limit in this architecture, or at least not in the way I expected. In fact, it sometimes feels worse or less predictable than using direct URLs with carefully managed pool sizes.
So I’m trying to confirm whether I’m missing something fundamental. Is the 100-connection limit truly global for the entire cluster regardless of how many databases are used? Is it expected that PgBouncer pooled connections still consume database connections and show up in the “Database Connections” metric? In a setup with many Phoenix apps and many databases, does PgBouncer in transaction mode actually reduce pressure on max_connections, or is its benefit mostly in other scenarios? And finally, in this kind of architecture, is using direct URLs with very carefully tuned pool_size values the only reliable way to stay under the limit?
Any clarification would be greatly appreciated. I want to make sure I’m interpreting the metrics correctly and following Fly best practices rather than fighting the platform. Thanks!