What a Managed Postgres Changes for Laravel
Serverless Postgres is priced and shaped for request-per-connection workloads. Laravel holds long-lived connections in its queue workers, and that mismatch is where the surprises are.
Managed Postgres used to mean a server somebody else patched. It increasingly means a platform: storage separated from compute, connections brokered by a pooler, databases that branch like git, and compute that suspends when nobody is asking.
All of that is built for a workload where a request arrives, runs a query and goes away. Laravel is that workload on the web tier and the opposite of it everywhere else.
The shape mismatch, stated plainly
A Laravel application has three kinds of database client and they behave completely differently:
Web requests. PHP-FPM, connect and disconnect per request. This fits the serverless model exactly.
Queue workers. queue:work connects at boot and holds that connection for
its entire life, whether or not it is processing anything. Twenty workers are
twenty permanent connections before a single visitor arrives.
The scheduler. A cron entry running schedule:run every minute, forever.
Providers price and size around the first one. The second is what fills a connection allowance, and it is the most common cause of a connection-limit error. The third is what quietly defeats scale-to-zero.
Scale-to-zero and the scheduler
This is the one people get wrong first and it is arithmetic rather than opinion.
Compute suspends after some idle period. schedule:run issues a query every
sixty seconds. If the idle threshold is longer than a minute, the database
never suspends and you are paying for always-on compute with cold-start
characteristics layered on top.
There are three honest responses. Accept it and size for always-on, in which case a conventional instance may be cheaper. Move the scheduler to something that does not touch the database when there is nothing due - which means the schedule definition has to be readable without a query. Or accept cold starts on a staging environment and not on production, which is where scale-to-zero genuinely pays.
Nobody should discover this from an invoice.
Pooling modes, which is the one that breaks things
Every serverless Postgres puts a pooler in front. The mode decides what your application can do.
Session pooling hands you a real backend connection for the life of your connection. Everything works. You also get far fewer effective connections, which is usually why you came.
Transaction pooling hands you a backend only for the duration of a transaction. This is what gives you thousands of client connections over a small number of real ones, and it removes anything that spans statements:
- Server-side prepared statements, which PDO uses by default. This is the one that bites Laravel specifically, and the fix is a connection-string flag or an emulation setting rather than a code change - but it has to be set deliberately.
- Advisory locks, which means
withoutOverlapping()on a schedule andShouldBeUniqueon a job may not behave as written if they are backed by the database. SETstatements, so anything that sets a timezone or a search path per connection stops holding.LISTEN/NOTIFY, and any long-lived cursor.
The practical arrangement is two connections in config/database.php: the
pooled endpoint for web requests, the direct endpoint for migrations, queue
workers and anything holding a lock. That is ten lines and it prevents most of
this article.
Branching, which is the genuinely new capability
A branch is a copy-on-write database at production scale, created in seconds. For Laravel that lands on a specific problem: migrations that are safe against forty rows and lock for four minutes against four million.
A branch per pull request, migrations run against it in CI, the timing recorded. Now a dangerous schema change fails a build instead of a deploy. This is the strongest argument for a platform over an instance, and it is worth more than the pricing model.
Choosing between them
A conventional managed instance - RDS, Cloud SQL, a provider's plain Postgres - when the application has a steady worker fleet, when you want predictable connection counts, and when the ops overhead you are buying away is patching rather than scaling. This is still the right answer for most business applications and it is unfashionable to say so.
Serverless Postgres with branching when the migration-testing story matters to you, when environments are numerous and short-lived, or when traffic is genuinely spiky. Budget the pooling work rather than assuming it is a connection-string change.
A platform with auth and storage attached when you are using those too. If you are using it only as a database, you are choosing a Postgres provider and should compare it as one.
Anything MySQL-compatible but sharded only with the constraint list read
first. Referential integrity, transactions that cross shards, and ORDER BY
across a sharded table are where the model shows through, and Laravel
migrations will happily write DDL that the platform accepts and does not
enforce.
The check that costs an afternoon
Before committing, run the real thing: a migration with an index build on a production-sized table, your actual worker count connected at once, and a scheduled command holding a lock. Three measurements, one afternoon.
Every problem in this article is cheap to find that way and expensive to find in the second month.
Where the provider's regions run is not only a latency question. New Zealand has a rule about sending data offshore that applies to us as much as to you. Kuwait classifies the data first and picks hosting per table afterwards. In both, the region list belongs in the schema conversation.
