Your Laravel app can slow down, throw 502/504 errors, or spike CPU when PHP-FPM workers exhaust RAM. Active processes then move into swap, making a larger Droplet an expensive guess.
Measure before you resize a droplet
Resize only after finding the real limit during a slowdown. Check for PHP-FPM memory pressure, MySQL latency, Redis contention, disk wait, or steady CPU saturation.
Read the host and app together
Check Laravel and host data in the same five-minute window. Run vmstat 1, free -m, iostat -xz 1, and top.
Also check slow requests, failed jobs, queue wait time, and NGINX upstream errors. These numbers show whether the host or app is the limit.
| Observed signal | Likely limit | Confirm it | First move |
|---|
| p95 rises, swap I/O appears | RAM pressure | `vmstat 1`, PHP worker RSS | Lower PHP-FPM and queue concurrency |
| CPU stays above 80% at peak | Compute saturation | CPU chart and APM traces | Profile code, then add dedicated CPU |
| CPU is low, MySQL spans are slow | Database I/O or query design | Slow query log, `EXPLAIN` | Add indexes or repair queries |
| Only public pages are slow for distant users | Network and cache misses | Cloudflare and TTFB data | Cache safe responses at the edge |
Test cold and warm caches
Run the same load tests with cold and warm Laravel, Redis, and database caches. This separates first-request cost from normal repeat traffic.
Use the MySQL slow query log as a production feedback loop. Do not treat it as a one-time troubleshooting tool.
Set a sensible long_query_time for the app. Review repeated query shapes, not isolated requests.
Run EXPLAIN or EXPLAIN ANALYZE on sample queries before adding an index. Watch for repeated relationship queries and unbounded result sets.
Also watch %LIKE% searches and sort or filter columns without an index. These patterns often increase Laravel response time.
A better index must reduce Laravel p95 response time under the same load. It may improve one endpoint but raise write cost.
Size PHP-FPM from worker memory
Calculate PHP-FPM capacity from measured worker memory. Use pm.max_children = available RAM for PHP-FPM / p95 PHP worker RSS.
Reserve RAM for Ubuntu, NGINX, MySQL, Redis, queues, file cache, and headroom. Each process needs space during a traffic peak.
Reserve RAM for every process
Measure worker RSS under traffic close to production. Reserve roughly 25% headroom before raising PHP-FPM or Horizon concurrency.
Safe sizing order: Measure p95 worker RSS. Reserve 25% headroom. Set `pm.max_children`, test peak concurrency, then review swap activity and p95 again. A clean CPU chart does not cancel active swap.
Cache boot work, then profile queries
Enable OPcache and optimized Composer autoloading. Also cache Laravel config, routes, and views.
Then profile query traces. Caching cannot fix missing indexes or slow external API calls.
Laravel latency triage flow
p95 and errors
→
Swap active?
→
Worker RSS
→
MySQL and Redis spans
→
Tune, split, or resize
Set OPcache options on purpose. Do not treat it as a simple on-or-off switch.
Production setups often use opcache.enable=1. They also set enough opcache.memory_consumption for the deployed codebase.
Many teams set opcache.validate_timestamps=0. This works when deployments reload PHP-FPM or reset OPcache.
Set pm.max_requests to recycle PHP-FPM workers after a controlled request count. A range of 500 to 1,000 is common.
Use that range when libraries or extensions show gradual memory growth. It limits distortion in worker memory estimates.
This does not fix a memory leak. It stops a few old workers from consuming reserved headroom.
Split queues before buying more CPU
Web requests, queue workers, and scheduled tasks need separate limits. Exports, imports, image work, and email batches can consume web resources.
Separate redis roles when needed
Check Redis latency, cache behavior, and session locking before blaming NGINX. Requests from one signed-in user can wait on a session lock.
A locked session can look like a slow application page. The CPU chart may still look normal.
Compare cost with the same test
Compare Droplet plans with the same traffic mix and test region. Compare p95, p99, throughput, and error-rate targets.
Dedicated CPU makes sense only after repeatable compute contention remains. First fix memory limits and query issues.
| Choice | Best when | Validate with | Operational tradeoff |
|---|
| Tune current Droplet | Swap, query, or cache issue | p95 drops after limits change | Requires profiling discipline |
| Larger or dedicated CPU Droplet | CPU exceeds 80% repeatedly | Same test improves p95 and p99 | Higher monthly cost, single-host risk |
| Separate worker Droplet | Long or bursty jobs | Web p95 falls while jobs run | More deployment and monitoring work |
| Managed platform such as Kinsta | Team needs less server operations | Support, uptime, and cost fit workload | Less low-level control than a VPS |
Do not treat one tuned Droplet as the main solution for multi-zone high availability. The same applies to automatic failover, major spikes, distributed storage, or HIPAA controls. A single-server tune cannot replace a code audit, especially when traces show slow queries, serialization, or external integrations.
Set Laravel queue concurrency from measured job behavior. Do not base it on vCPU count alone.
For each queue class, measure median and p95 runtime. Also measure PHP worker RSS, Redis latency, retry volume, and free RAM.
Reserve web PHP-FPM capacity before sizing workers. That prevents background jobs from harming customer requests.
In Laravel Horizon, separate CPU-heavy or high-RSS jobs into their own supervisors. Examples include exports, image work, and report generation.
Give these supervisors lower maxProcesses values than email or webhook queues. Light jobs and heavy jobs should not compete equally.
If one worker uses 250 MB at p95, only 1 GB may remain. Four concurrent workers are unsafe because they can cause Linux memory pressure.
Raise Horizon concurrency slowly. Check that queue wait time falls without new swap activity.
Also check Redis contention, NGINX upstream errors, and web p95 latency. Faster jobs should not make customer pages slower.
Your questions answered
How do I know if my Laravel Droplet needs more RAM?
Your Droplet may need more RAM when swap I/O continues after lower worker limits. Measure PHP-FPM, MySQL, Redis, and job-worker memory first.
Confirm pressure at peak traffic with vmstat 1, per-process RSS, free -m, and OOM logs. Otherwise, tune or move the process causing pressure.
What PHP-FPM max children value should Laravel use?
Set it from available PHP RAM divided by p95 worker RSS. Reserve headroom, then test latency and swap under expected concurrency.
Can redis caching fix a slow laravel app?
Redis can speed repeat reads, sessions, and queues. It cannot repair slow MySQL queries or external API calls.
Should I move Laravel from DigitalOcean to Kinsta?
Move when an optimized Droplet misses p95, uptime, support, or compliance targets. Kinsta trades control for less server administration.
Is Laravel Octane faster than PHP-FPM?
Octane can reduce framework boot time. It does not fix missing indexes, slow Eloquent queries, or memory growth.
When should I separate Horizon workers from web?
Separate workers when jobs raise web p95 latency, swap, or 502 errors. This often happens during exports, imports, or image processing.
The essential points:- Measure p95, p99, swap I/O, CPU, disk wait, and database latency during a real peak.
- Set PHP-FPM workers from measured memory consumption, not vCPU count.
- Limit and separate Horizon, Scheduler, and web workloads before buying more CPU.
- Resize or change providers only when identical tests show a measurable p95, error-rate, uptime, or operational benefit.