▸ Free tool
Postgres Connection Pool Calculator.
Count the Postgres connections your Laravel app can open at peak, compare them with max_connections, and get PgBouncer settings that fit.
▸ Defaults checked October 5, 2026
Postgres and PgBouncer defaults come from their docs. Pool sizes are rules of thumb, so load test before you rely on them.
Peak connections / usable slots
0 / 0
- Slots used
- 0%
- max_connections for direct use
- 0
With PgBouncer, transaction mode
- default_pool_size
- 0
- reserve_pool_size
- 0
- max_client_conn
- 0
- Server connections used
- 0
Live as you type · runs in your browser
Where they come from.
| Role | Servers | Processes | Connections |
|---|
pgbouncer.ini
config/database.php
How the calculator counts
Every PHP process that runs a query holds one connection per database connection it uses. The calculator adds them up per role:
- Web: servers ×
pm.max_childrenfor PHP-FPM, or servers × Octane workers. FPM children close their connection when the request ends, so this is the peak with every child busy. Octane workers keep theirs open. - Queues: servers × worker processes. Horizon and
queue:workare long running, so each process keeps a connection. - Scheduler: scheduled commands that can run at the same time, plus anything else that connects: migrations, a psql session, backups, monitoring.
- Usable slots:
max_connections - superuser_reserved_connections - reserved_connections. The direct budget adds 20% headroom for deploys, when new workers start before old ones stop.
Worked example: two web servers with pm.max_children = 20, one Horizon box with 10 processes, two overlapping scheduled commands
and 5 other clients come to 40 + 10 + 2 + 5 = 57 connections. With the
default 100 and 3 reserved, that is 59% of 97 slots. Add a third web
server or switch to Octane with a read/write split and you pass 100.
PgBouncer in transaction mode
PgBouncer lets hundreds of clients share a small pool of real
connections. In pool_mode = transaction a server
connection goes back to the pool when a transaction ends, so a mostly idle
FPM child or queue worker no longer holds a Postgres backend. The pool only
needs to cover queries running at the same moment, which is why the suggestion
is about twice the database's CPU cores, from the PostgreSQL wiki's (cores × 2) + spindles rule, with a small reserve for bursts.
Two things change for Laravel. Prepared statements: PDO prepares on the
server, and PgBouncer 1.21 and newer handle that when max_prepared_statements is above 0 (200 by default since 1.24). On an older PgBouncer, set PDO::ATTR_EMULATE_PREPARES => true. Session state: SET without LOCAL, advisory locks, LISTEN and temporary tables
do not survive between transactions. Keep those in one transaction, or give
them a separate pool in session mode.
Sources, checked October 5, 2026: Postgres connection settings, PgBouncer configuration, PgBouncer changelog, PostgreSQL wiki: number of connections, Laravel Octane worker count, PDO_PGSQL.
Questions, answered
How many connections does a Laravel app open to Postgres?
One per PHP process that talks to the database, per database connection it uses. A PHP-FPM child opens it while it serves a request and closes it at the end, so the peak is servers times pm.max_children. Octane workers, Horizon workers and queue:work processes are long running and keep their connection open the whole time. A read/write split or a second connection in config/database.php doubles the count.
What should max_connections be?
The Postgres default is 100, with 3 slots kept for superusers. Raising it works up to a point, but every connection is its own backend process and Postgres sizes some shared memory from max_connections, so idle connections still cost memory. If your peak is far above a few hundred, put PgBouncer in front instead of raising the limit again.
What is a good PgBouncer default_pool_size?
Start near twice the CPU cores of the database server. The PostgreSQL wiki suggests about (cores × 2) + effective spindles active connections for throughput, and spindles count as zero when your data fits in memory. PgBouncer's own default is 20. Add a small reserve_pool_size for bursts, then load test and watch SHOW POOLS for clients waiting.
Do prepared statements work with PgBouncer transaction mode?
With PgBouncer 1.21 or newer and max_prepared_statements above 0, yes. It tracks the protocol level prepared statements that PDO uses, and 1.24 turned this on by default with a value of 200. On older versions, set PDO::ATTR_EMULATE_PREPARES to true in the options of your pgsql connection, or you will see errors like prepared statement does not exist.
What breaks in transaction pooling mode?
Anything that expects the same session across transactions: SET without LOCAL (for example a per-tenant search_path), session advisory locks, LISTEN and NOTIFY, temporary tables and WITH HOLD cursors. Wrap those in a transaction with SET LOCAL, or send that traffic to a separate pool in session mode. Normal Eloquent queries and DB::transaction work fine.
Is anything I type sent to a server?
No. The page does the maths in your browser and keeps your numbers in the address bar so you can share the result. There are no secrets in it: only counts and settings.
▸ Last verified:
Need this in production?
Worried about scale or cost? My architecture audit finds the bottleneck before your users do.
Architecture Audit
Also related: Laravel SaaS MVP .
Keep reading
-
▸ Tool
Server Capacity Calculator
Work out how many workers your traffic needs before you count their connections.
-
▸ Tool
Self-Hosting Cost Calculator
Price the servers that run Postgres and PgBouncer yourself.
-
▸ Post
Laravel Octane for high traffic
Why long-running workers change your connection count.
-
▸ Post
Horizontal vs vertical scaling
Every new app server adds connections. Plan for it.