Our services

If you can think it, we can make it brainsoft.

What breaks first when a Laravel app gets more traffic

Written By: BrainSoft In Backend

A Laravel application running on a single virtual server handles moderate traffic with zero complaints. Then a marketing push lands, concurrent users jump from twenty to five hundred, and response times crawl from sixty milliseconds to twenty seconds before the web server starts throwing 502 Bad Gateway errors.

When traffic spikes, PHP-FPM worker saturation and database connection exhaustion almost always take Laravel down first. File-based sessions lock processes, unindexed Eloquent queries choke MySQL, and queue workers starve because synchronous web requests consume all available server threads before jobs ever reach the background.

PHP-FPM worker pool exhaustion

By default, many deployments configure PHP-FPM with dynamic process management and conservative limits, often between ten and thirty worker processes. Every incoming HTTP request claims a dedicated worker until PHP finishes executing, transmits the response, and cleans up memory.

If an endpoint takes 200 milliseconds to complete, thirty workers can theoretically handle 150 requests per second. The moment a third-party API slows down or a database query stalls, request duration doubles. The queue of waiting requests backs up in Nginx, memory fills up, and PHP-FPM stops accepting new connections entirely. Switching to static process management with calculated memory ceilings helps, but only if you also strip slow operations out of the synchronous request cycle.

Session and cache storage bottlenecks

Fresh Laravel installations use the file driver for sessions and cache. Under low traffic, reading and writing small files to storage/framework/sessions is fast enough that nobody notices. Under concurrent load, it becomes an immediate operational bottleneck.

Operating systems struggle with file locking when dozens of requests try to write to the same session store simultaneously. Moving sessions and cache to Redis eliminates disk contention, but misconfigurations here introduce new failure modes. If your team relies on our DevOps and web engineering work across our services, you have likely seen un-pipelined Redis commands or a shared cache instance run out of memory when key evictions are not properly configured.

Database connections and hidden query patterns

Eloquent makes relational queries simple to write, which hides performance traps until table sizes and request volumes grow together. Two specific issues tend to crash the database tier under sustained traffic:

  • Max connections reached: Each PHP-FPM process holds an open connection to PostgreSQL or MySQL during execution. One hundred active web workers easily exceed default database connection pools, causing immediate connection refused exceptions.
  • N+1 queries and missing composite indexes: A page that runs twenty queries against a table with ten thousand rows feels instantaneous locally. Multiply that by three hundred concurrent users on a table with two million rows, and the database server exhausts its CPU running table scans.
  • Unbuffered writes: Tracking user activity or updating hit counters directly inside web requests creates row-level locks that block subsequent reads.

Frequently asked questions

Should I switch to Laravel Octane as soon as traffic increases?

Octane keeps the application in memory using Swoole or RoadRunner, which eliminates framework boot overhead. However, it does not fix unindexed database queries or slow external API calls, and it introduces state-leak bugs if your code is not built for persistent memory runtimes. Fix your queries and cache layer before changing your runtime architecture.

Why are my queue workers failing under heavy site traffic?

Queues often share the same database or Redis server as web requests. When the database becomes saturated by frontend traffic, background workers timeout trying to claim or update jobs. Giving queues an isolated Redis instance or a dedicated database connection prevents web spikes from halting background tasks.

How do I determine the right number of PHP-FPM workers?

Check the average memory consumed by a single PHP worker under load using system monitoring tools. Subtract the memory reserved for the OS, database, and background services from your total RAM, then divide the remainder by that per-process average to establish a safe max_children value.


#Backend