Stop Letting Apache Prefork Kill Your RAM: The Definitive Guide to Tuning PHP-FPM
It is 2012, yet I still see seasoned sysadmins treating their web servers like it's 2005. If you are running a high-traffic Magento store or a heavy Drupal site on a VPS using the default Apache prefork MPM with mod_php, you are essentially setting money on fire. Every time a client requests a 2KB image, your server spawns a bloated Apache process carrying the entire PHP interpreter payload. It is inefficient. It kills your concurrency. And when your RAM runs out, your server hits the swap, and your site latency spikes from 200ms to 10 seconds.
There is a better way. It is called PHP-FPM (FastCGI Process Manager).
At CoolVDS, we see this daily. Clients migrate from shared hosting or budget VPS providers where they were hitting resource limits. They think they need a bigger server. Usually, they just need a better architecture. Today, we are going to configure Nginx and PHP-FPM to handle thousands of concurrent connections without melting your CPU.
The Architecture: Why FPM Matters
Unlike mod_php, which embeds PHP inside Apache, PHP-FPM runs as a standalone daemon. Nginx (which handles static files and concurrency far better than Apache) proxies dynamic requests to FPM via a Unix socket or TCP/IP. This decoupling allows you to tune the PHP process pool independently of the web server.
Pro Tip: Using Unix sockets (.sock) avoids the TCP/IP overhead of localhost loopbacks, shaving off crucial microseconds. In our Oslo datacenter tests, sockets outperformed TCP by roughly 8% under heavy concurrency.
Step 1: The Installation (Debian/Ubuntu & CentOS)
Let's assume you have a clean VPS running Ubuntu 12.04 LTS or CentOS 6.3. We aren't going to compile from source today—package managers have caught up.
Ubuntu 12.04
sudo apt-get update
sudo apt-get install nginx php5-fpm php5-mysql php-apc
CentOS 6 (requires EPEL/REMI repositories)
rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
rum -Uvh http://rpms.famillecollet.com/enterprise/remi-release-6.rpm
yum --enablerepo=remi install nginx php-fpm php-mysql php-pecl-apc
Step 2: Configuring the Process Manager (The Meat)
This is where most installations fail. The default configuration is designed for a tiny server, not a production beast. Open your pool config file. On Ubuntu, it is usually /etc/php5/fpm/pool.d/www.conf.
You have three choices for the pm (Process Manager) setting:
- static: A fixed number of child processes. Best for dedicated hardware where you know exactly how much RAM is available.
- dynamic: Scales processes up and down based on demand. Good for fluctuating traffic.
- ondemand: Spawns processes only when requested. Saves RAM, but introduces latency on startup. Avoid for high-traffic sites.
For a reliable CoolVDS instance with 4GB RAM, I recommend dynamic or static. Here is a calculated configuration for a server dedicated to the web stack:
; /etc/php5/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
; We use Unix sockets for speed
listen = /var/run/php5-fpm.sock
; Process Manager Settings
pm = dynamic
; Total RAM / Average Process Size = max_children
; Example: 3000MB Free RAM / 60MB per PHP process ~= 50
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15
; Kill processes after 500 requests to prevent memory leaks
pm.max_requests = 500
; Slow log is critical for debugging
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log
Do not guess these numbers. Run top or htop during a load test. Check the RES column for your php-fpm processes. If they average 80MB and you have 2GB of RAM, setting max_children to 100 will cause your server to swap to disk. Once you hit swap, performance is dead.
Step 3: Nginx Configuration
Now we tell Nginx to talk to our optimized FPM pool. This goes inside your server block in /etc/nginx/sites-available/default.
server {
listen 80;
server_name example.no;
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Connect to the socket defined in www.conf
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# Tune buffers to prevent writing to disk for large headers
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
}
}
Step 4: APC - The Silver Bullet
In 2012, running PHP without an opcode cache is negligence. PHP is an interpreted language; it compiles your script to opcodes on every single request. APC (Alternative PHP Cache) stores these opcodes in shared memory.
Edit your php.ini or apc.ini:
extension=apc.so
apc.enabled=1
apc.shm_segments=1
; Allocate enough memory to store all your PHP files.
; 128M is a good starting point for Magento/Drupal.
apc.shm_size=128M
; 0 means never expire cached files unless they change.
apc.ttl=7200
apc.user_ttl=7200
apc.stat=1
Without APC, a simple WordPress request might take 200ms of CPU time. With APC, that drops to 40ms. It is that significant.
Hardware Reality Check: SSD vs. HDD
Even with perfect tuning, your database is often the bottleneck. Traditional 7.2k RPM SATA drives or even 15k RPM SAS drives struggle when MySQL pushes high IOPS (Input/Output Operations Per Second).
This is where CoolVDS differentiates itself. We don't just use standard storage; we utilize high-performance SSD storage arrays (often utilizing PCIe flash technology similar to what you see in enterprise Fusion-io cards). In a read-heavy environment like a web server, the random read performance of an SSD drastically reduces the "wait time" for database queries.
| Metric | Standard SATA HDD | CoolVDS SSD Storage |
|---|---|---|
| Random IOPS | 75 - 120 | 10,000+ |
| Latency | 5ms - 15ms | < 0.1ms |
| Boot Time | 45 seconds | 8 seconds |
Local Considerations: Latency and Law
If your customers are in Oslo, Bergen, or Trondheim, hosting in Germany or the US adds unavoidable network latency—often 30ms to 100ms. CoolVDS infrastructure is physically located in the Nordics, peering directly at NIX (Norwegian Internet Exchange). Your packet round-trip time is minimized.
Furthermore, complying with Datatilsynet guidelines regarding personal data storage is simpler when your physical servers are within Norwegian jurisdiction. While the EU Data Protection Directive applies across Europe, local data sovereignty is becoming a serious talking point for businesses handling sensitive customer records.
Conclusion
Performance isn't magic. It is math. By moving to Nginx and PHP-FPM, utilizing APC caching, and ensuring your underlying infrastructure uses solid-state storage rather than spinning rust, you can handle 10x the traffic on the same budget. Stop letting mod_php dictate your scalability.
Ready to test this stack? Deploy a CentOS 6 or Ubuntu 12.04 instance on CoolVDS today. With our low latency network and pure SSD storage, your PHP apps have never run faster.