Running your application
Handling peaks
Prepare an environment for a burst of requests, such as a ticket sale or a vote: replicas, PHP threads, the database, open connections.
On this page
A peak is a burst of requests that comes at a known moment: a ticket sale that opens at 10:00, a vote during a broadcast, a mailing. An environment that sleeps does not catch one cold. Prepare it before the moment, and give it back afterwards.
How much an environment can answer
Every replica of the web container runs FrankenPHP with two PHP threads per vCPU, and each thread answers one request at a time. Roughly:
requests a second ≈ replicas × threads per replica ÷ seconds per request
20 replicas of 1 vCPU have 40 threads. At 100 ms a request that is about 400 requests a second; at 1 s a request, 40. A request that waits on the database holds its thread while it waits, so the time a request takes is what decides it, more than the processor.
What a test environment did with a vote endpoint that stores each vote in a Serverless SQL Database before it answers (November 2026):
| Set-up | Offered | Answered | Median |
|---|---|---|---|
| 20 replicas of 1 vCPU, a new database connection per request | 500/s | 184/s | 6.3 s |
| The same, connections kept open | 500/s | 335/s | 67 ms |
| 20 replicas of 2 vCPU, connections kept open | 500/s | 492/s | 99.9% accepted |
| From zero replicas, nothing prepared | 1,000/s | 7% in the first 10 s |
The database itself stayed around 1 vCPU: the threads were the limit, not PostgreSQL.
Before the peak
- Start replicas in advance. A replica that starts during the peak makes the first requests wait for it. Raise
web.mininbrewless.ymland deploy, or set it in the console or withbrewless scale, some minutes before the moment; see Scaling. Raiseweb.maxtoo when the formula above says you need more. - Give the replicas more processor.
web.cpu: 2000gives each replica 2 vCPU and so four threads instead of two; see brewless.yml. - Boost the database. A Serverless SQL Database scales up as the load grows, from a minimum that is low unless you raised it. Plan a boost with time to warm up, so that a restart for a large raise happens before the peak and not during it:
brewless db:boost production --min=4 --max=8 --for=2h --at=09:30. See Database capacity. - Keep database connections open. Without it, every request opens a new TLS connection to the database first. See below.
Afterwards, put web.min back and let the boost end, or the replicas and the database keep costing what they cost during the peak.
Connections that stay open
With brewless/laravel 1.5 or later, the plain variable BREWLESS_DB_PERSISTENT keeps the PostgreSQL and MySQL connections open between requests:
environments:
production:
variables:
BREWLESS_DB_PERSISTENT: 'true'
A variable reaches the containers with the next deploy. Each PHP thread then keeps one connection, also while the container is quiet, so the number of connections is replicas × threads: 20 replicas of 2 vCPU hold 80. Keep that under what the database accepts. Scaleway gives a Serverless SQL Database more connections as it gets more vCPU, up to 1,000; an open connection does not keep it awake.
What the package takes care of:
- A connection that an earlier request left in the middle of a transaction is rolled back before the next request uses it.
- After the database restarted, for a boost, a failover or maintenance, the first query on an old connection fails as a lost connection and is tried again on a new one, for up to
BREWLESS_DB_RETRY_SECONDS. - A setting of the session (
SET …) stays with the connection and reaches the next request on that thread. Do not rely on one; on Serverless SQL sessions are shared anyway. See Databases.
A queue in between
Answering at once and storing the vote from a queued job feels faster, but the jobs still have to be stored by the same database, by fewer workers. In the test above it only paid off with more workers (worker.max) or with jobs that store many votes in one insert. Without that the queue grows during the peak and empties long after it.