What's the best way to run PHP in Debian in a shared environment?

Years ago May First/People Link switched from the old reliable mod_php to suPHP because mod_php ran all php scripts as the same user, meaning (among other things) that any site could access the database configurations of any other site.

suPHP was great, however, after several years we've decided to replace it because:

  • As our members increasingly use Drupal and Drupal sites grow increasingly more complex, suPHP's performance has become a problem.
  • suPHP executes with the permissions of the owner of the file it is executing. From a zero-conf perspective, this features makes suPHP very easy to setup. However, from a security perspective, it forces you to run your php script with the permission to delete itself. Not optimal.

We first turned to fcgid. We tried it out on a couple individual web sites (fcgid can run alongside suPHP), and, after a week without much problems, we implemented it on an entire server.

Out of the box, on a shared server with several dozen web sites, the most notable resource utilization difference between fcgid and suPHP is the increase in memory usage. Unlike suPHP which dies after each requests, fcgid gains a performance boost by sticking around (with either 2 or 3 processes living on) to server future requests. If you have several dozen web sites that aren't high traffic sites, it means they may each have up to the three processes staking out memory that would otherwise be free with suPHP.

We addressed the memory problem by adding the following to /etc/apache2/mods-available/fcgid.conf:

DefaultMinClassProcessCount 0
IdleTimeout 60
IdleScanInterval 60

These directions tell fcgid to kill processes that have been idle for more than 60 seconds. That means we still get the performance gain from a site getting more than one page view per minute, but otherwise we re-claim the memory.

Before switching our remaining servers we decided to do more extensive testing to ensure we would be getting the performance and resource gains we were hoping. Also, we decided to consider mpm-itk, which is a variation of mpm-prefork that runs each virtual host with a per-vhost configured user and group (like suexec, but for the entire virtual host). Since it's a variation of mpm-prefork, it's safe to run it with the more resource friendly mod_php.

We used the apache benchmarking tool ab. And we tested on a virtual machine (running on my laptop) with 1GB of RAM. The test web site we used was a copy of the May First/People Link site (we used the home page for the test), which is running Drupal 6. It's a relatively simple Drupal site, however, we turned Drupal caching off to better simulate PHP processing usage likely to be found on our members' sites.

We ran tests against the following configurations:

  • Plain mpm-prefork with mod_php and apc (as a standard to strive for)
  • Both mpm-worker with suPHP and mpm-prefork with suPHP (how much of a difference does worker vs prefork make with a Drupal site?)
  • mpm-worker/fcgid/suexec
  • mpm-itk with mod_php

The tests were run with:

  • 1 request (in the results, this test is labelled 1:1)
  • 20 requests, 5 concurrent (20:5)
  • 50 requests, 10 concurrent (50:10)
  • 70 requests, 10 concurrent (70:10)

We originally tried, for the last test, 100 requests/25 concurrent but it caused ab to time out on the mpm-worker/fcgid/suexec configuration. We then lowered it to 75/15. Still timed out (load on our test server went way up).

The tests paused 120 seconds between testing each environment to give the server load a chance to settle down. And we paused 30 seconds between each test (not enough time for the fcgid processes to be killed, but enough time for all the processes to hopefully complete their requests).

The tests are scripted and the full results are available. You can also see the various vhost configurations (test.mouse, test.mouse.itk, test.mouse.suexec.fcgid), and the fcgid.conf.

To summarize the report ... if you are running one web site on a server, there's little argument for using anything other than mod_php and mpm-prefork - the performance of this combination far exceeds any other combination. I'm scratching my head over the the vastly different conclusions reached by 2bits. They didn't publish their tests so I'm not sure exactly how to compare. Maybe they have a lot more static content? Or - maybe there's a mistake in our tests? Open to ideas.

suPHP seems to be left in the dust compared with either fcgid/suexec or mpm-itk. Given that it's also less flexible, there's not much going for it.

And lastly... fcgid/suexec and mpm-itk seem comparable. That was a surprise. We were expecting mpm-itk to perform better because it's running mod_php. Given the similarity in performance, fcgid/mpm-worker seems like a much more well-tested and well-supported approach for a shared server.

One side note... originally, out of laziness, we tested against the default Drupal install.php page. Less static content and much less PHP processing. In these tests, with low loads, the results provide different numbers, but the comparisons between environment were similar ... except with the last test. With the 70 requests/10 concurrent test, fcgid/suexec dramatically out-performed everyone (by a factor or more than 2). That's probably because no new cgi processes had to be spawned and the effect of mod_php using APC was reduced because there was very little php code to process.