Skip to content
ansezz.

▸ 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.

Web

A child holds a connection only while it serves a request.

Queues

Horizon maxProcesses, summed over supervisors.

Scheduler and the rest

Commands that overlap, e.g. runInBackground.

2 with a read/write split.

Migrations, psql, backups, monitoring.

Postgres and PgBouncer

Postgres default: 100.

Default: 3.

Postgres 16+, default 0.

Physical cores, not threads.

One per database and user pair.

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_children for 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:work are 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