The Apache bloat is killing your throughput
I saw it happen again last Tuesday. A client running a moderate Magento store on a standard LAMP stack got hit with a traffic spike. Nothing massive—just a localized marketing push here in Norway. The result? Load averages climbed to 25.0, the server started swapping to disk, and the site became as responsive as a brick. The culprit wasn't the code; it was the architecture.
If you are still serving static files through Apache while it carries the overhead of the embedded PHP interpreter (mod_php) for every single request, you are wasting memory. It’s 2011. We have better ways to handle concurrency.
The solution isn't adding more RAM—that’s a band-aid. The solution is moving to an event-driven architecture using Nginx and PHP-FPM (FastCGI Process Manager). This setup separates the web server from the PHP processing, drastically lowering the memory footprint per connection.
Why PHP-FPM changes the game
With mod_php, every Apache child process consumes roughly the same amount of RAM, whether it's serving a complex PHP script or a 1KB style.css file. It's inefficient. Nginx acts as a lightweight proxy, handling thousands of static file connections with just a few megabytes of RAM. It only passes the actual PHP requests to the PHP-FPM backend via a Unix socket or TCP.
Since PHP 5.3.3, FPM is now part of the core. It offers features we desperately needed for years:
- Adaptive process spawning.
- Advanced logging for slow scripts (finally).
- Emergency restart in case of accidental opcode cache destruction.
Configuration: Tuning the Pool
Installing it is straightforward on a CoolVDS CentOS 5 instance using the Remi or EPEL repositories. But the default config will still bottleneck you if you don't touch it. The magic happens in /etc/php-fpm.d/www.conf.
Here is the configuration I use for production environments handling bursty traffic:
; /etc/php-fpm.d/www.conf
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
request_terminate_timeout = 30s
The Breakdown:
- pm = dynamic: This is crucial. It means we don't keep unused processes in memory, but we can scale up instantly when the request hits.
- pm.max_children: Calculate this carefully. If your average PHP process takes 30MB and you have a 2GB VPS, don't set this to 100. Leave room for the OS and the database. On a CoolVDS 1024MB plan, a value of 20-30 is usually the sweet spot.
Pro Tip: Don't forget the Opcode Cache. PHP-FPM is fast, but compiling PHP scripts on every request is slow. Install APC (Alternative PHP Cache). Setapc.shm_size=128Min yourphp.ini. This single change can double your requests per second.
The Nginx Handoff
You need to tell Nginx to pass `.php` files to the FPM daemon. Avoid the "If is evil" rewrite logic. Use a clean `try_files` directive.
server {
listen 80;
server_name example.no;
root /var/www/html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Hardware Matters: IO Wait is the silent killer
Even with perfect FPM tuning, your database can choke if the disk I/O is slow. This is where the underlying infrastructure becomes critical. Shared hosting often oversells disk I/O, leading to "noisy neighbor" issues where another user's backup script kills your site's performance.
At CoolVDS, we utilize KVM virtualization. This provides true isolation. More importantly, we prioritize low-latency storage subsystems. When your MySQL database is hammering the disk, you need consistent IOPS, not