Quick Setup: Apache Worker, FastCGI, PHP-FPM and APC

Sunday, January 29, 2012 - 07:50
  • Apache feather
  • PHP Logo
  • Ubuntu circle of friends logo.

I'm not going to go into great depths in this post, I'm mainly going to go through setting up Apache MPM Worker, PHP-FPM, FastCGI (mod_fastcgi, not mod_fcgid) and APC with an almost minimal configuration. I'm using Ubuntu 11.04 to demonstrate, though the steps should work for most Debian based, and generally most distro's apart from the package management and location of configuration files. I know 11.04 is a bit out of date but I'm waiting for Precise Pangolin to be released to upgrade to a LTS. This setup also allows the APC cache to be shared among PHP processes.

The point of all of this was to change away from Apache Prefork to Apache Worker as serving static files with mod_php running in a limited memory environment is devastating to available memory. I am quite happy with this set up so far, and haven't had problems with it at all.

It is much more common to see this set up with nginx rather than Apache, and I may go that route eventually but it is useful to not have to translate Apache configurations to nginx configurations when I am just testing something out, or someone else has to get their hands dirty.

Installing the necessary packages

On Ubuntu this is accomplished by:

  1. sudo apt-get install apache2-mpm-worker libapache2-mod-fastcgi php5-fpm php-apc php5-gd

It will also cause a few other dependencies to be installed, and you need the multiverse repository enabled to get the FastCGI package. The GD library is not strictly necessary but it allows the APC status page to make its' graphs.

Apache Configuration

I found that mod_fastcgi was automatically enabled upon install, but just to be sure we enable it anyway, along with mod_actions which we will be using.

  1. sudo a2enmod fastcgi actions

I added the following to /etc/apache2/httpd.conf, but I'm sure you could also add it to a specific virtualhost. I also created the directory /var/www/fastcgi, you may also have change its' ownership to be the same as the Apache or PHP-FPM processes so they can access the directory, it's usually www-data on Ubuntu. We are also using Unix sockets to connect to the PHP-FPM process as this has less overhead than going through the TCP/IP stack.

  1. <IfModule mod_fastcgi.c>
  2. Alias /php5.fastcgi /var/www/fastcgi/php5.fastcgi
  3. AddHandler php-script .php
  4. FastCGIExternalServer /var/www/fastcgi/php5.fastcgi -socket /var/run/php-fpm.sock
  5. Action php-script /php5.fastcgi virtual
  6.  
  7. # This part is not necessary to get it to work, but it stops anything else from being
  8. # accessed from it by mistake or maliciously.
  9. <Directory "/var/www/fastcgi">
  10. Order allow,deny
  11. <Files "php5.fastcgi">
  12. Order deny,allow
  13. </Files>
  14. </Directory>
  15. </IfModule>

In short it tells Apache to send all PHP files off to the PHP-FPM process. Seeing as we are using FastCGIExternalServer, FastCGI will let PHP-FPM do it's own process management.

PHP-FPM Configuration

We will be using the default process pool that is already set up, so we just need to change one line to tell PHP-FPM to listen for incoming connections on the same socket as we used in the Apache configuration. We are editing /etc/php5/fpm/pool.d/www.conf in this case.

  1. ; Find the line below
  2. listen = 127.0.0.1:9000
  3. ; and change it to this:
  4. listen = /var/run/php-fpm.sock

If you are in a low memory environment you will really want to adjust the process management settings. I left mine with dynamic process management and lowered the rest of the settings, if you don't set pm.start_servers, PHP-FPM will complain when you restart it, and set it to 20, which may be more than you need or have memory for.

PHP-FPM Status Page

If you want to see the status page for this PHP-FPM pool, you also need to uncomment the following 2 lines in /etc/php5/fpm/pool.d/www.conf:

  1. pm.status_path = /status
  2. ping.path = /ping

The status page contains a few lines of information about the process pool, such as active and idle processes, and accepted connections. My main use of it though is to check that the maximum number of children the pool can have has not been reached, which would mean that some request might have been denied as there were no spare processes.

To actually access the status page you will need to add the following to a virtualhost, within a directory:

  1. <FilesMatch "^ping|status$">
  2. SetHandler php-script
  3. </FilesMatch>

You can change ping and status to something else if you prefer but remember to change it in the pool configuration file and in the above snippet. I wouldn't put this somewhere publicly accessible but is useful to have around.

APC

APC should already be enabled after installing, we need to edit it's configuration file to set a larger memory limit and to use shared memory. The file in this case is /etc/php5/fpm/conf.d/apc.ini, and we add the following 2 lines:

  1. apc.mmap_file_mask=/apc.shm.XXXXXX
  2. apc.shm_size=96

This sets the memory limit to 96mb and makes APC use shared memory. In later versions of APC you may need to add a 'M' after the memory size setting or it will complain. While this has worked fine for me on normal Ubuntu installations, I had to edit /etc/fstab on our OpenVZ VPS and add the following:

  1. none /dev/shm tmpfs defaults 0 0

What is very useful when dealing with APC is to copy /usr/share/doc/php-apc/apc.php.gz to a web accessible folder and unzip it. It gives you an overview of how APC is doing and is very useful for troubleshooting, plus you get graphs. It should definitely not be publicly accessible. You will need to edit it to add a password if you want to be able to login to clear the cache but the file is well commented for that.

Using PHP-FPM along with APC will allow the APC opcode cache to be shared among all the PHP processes in the same pool. If you wish to use APC but are wary about sharing the APC opcode cache among different sites, simply create a new PHP-FPM process pool for each site and specify the pool in the relevant virtualhost. You can also specify most PHP configuration options on a per pool basis and run PHP as a different user or group per pool. You may even want to disable APC for a process pool if you wished.

Finishing Up

Issue the following command to restart PHP-FPM and Apache:

  1. sudo service apache2 restart && sudo service php5-fpm restart

If everything went alright you should now be able to serve PHP files with a great reduction in memory usage over Apache Prefork. I couldn't produce a repeatable, significant performance difference in using Apache Worker and PHP-FPM over Prefork and mod_php, though my testing was by no means conclusive. I used ab to access a basic Drupal page, and a static HTML page and requests per second were improved on both given limited resources, but I seem to have mislaid the results.

As a final note you may be able to reduce your Apache processes size further by setting 'ThreadStackSize 192000' in apache2.conf but this can cause severe crashes of the webserver and corruption of requests, the amount you can reduce the thread stack size to depends on what modules your Apache setup is using and so changing this setting should be well tested before going into production. According to the Apache 2.2 documentation, 128000 is too low on Linux, and from my experience 136000 is also too low. Though using 192000 reduces our 64 bit Ubuntu VPS's Apache processes size from around 22mb each to around 8 - 10mb each without ill effect.

It also goes without saying you can reduce your Apache process size by disabling unneeded modules.

Thanks for reading, and hopefully see you another time. :)

Author: Ryan Solomon
Ryan is a partner at desiDev, he likes open source software and QfG meeps.

Thanks for this post, it got

Thanks for this post, it got me headed in the right direction.

A couple things I had to do in addition to your steps was change permissions on /var/www/fastcgi (chown www-data fastcgi and chmod 666 fastcgi), and uncommented the lines in /etc/php5/fpm/pool.d/www.conf regarding socket settings:

  1. ; Set permissions for unix socket, if one is used. In Linux, read/write
  2. ; permissions must be set in order to allow connections from a web server. Many
  3. ; BSD-derived systems allow connections regardless of permissions.
  4. ; Default Values: user and group are set as the running user
  5. ; mode is set to 0666
  6. listen.owner = www-data
  7. listen.group = www-data
  8. listen.mode = 0666

I was also getting this warning in my php5-fpm.log:
WARNING: [pool www] server reached pm.max_children setting (10), consider raising it

I resolved that by changing pm.max_children to 20 in /path/to/www.conf. So far so good!

I’m glad you got some use out

I’m glad you got some use out of it!

I did mention that the permissions on /var/www/fastcgi may have to be changed to the correct user, but I’m not nearly as au fait with different distro’s users as I once was so I did not provide a definitive answer there, thanks for pointing out it needs to be clarified.

May I ask which distro and version you are using? I found Ubuntu 11.04 defaulted to those settings without needing to be set explicitly.

Ah, that’s just the maximum number of PHP process the PHP-FPM pool can run at once, you must be getting more traffic than the original VPS I set this up on as it never exceeded 8. /insert jealousy

And thanks for commenting :)

We’re running Ubuntu 12.04.

We’re running Ubuntu 12.04.

One other note– I’m not seeing the kind of performance improvement I was expecting,
so I went through some other tutorials and noticed a lot are using “AddHandler php-fastcgi” and “Action php-fastcgi” instead of “AddHandler php-script” and “Action php-script”. What’s the difference between using php-script and php-fastcgi?

Ah, thanks, I’m going to be

Ah, thanks, I’m going to be setting a 12.04 VPS up soonish.

As far as I know it makes no difference what you call the handler. In this case it is just a way to send all the PHP files to the PHP-FPM process. The PHP files are given the handler (might be easier in this case to think of it as a handle, or way to identify the file later on) with AddHandler, which just identifies them in order to call the Action, which uses the same handler. You could name it just about anything, if you changed them both to closet-python-script it should work just fine.

Edit: Forgot to include the predefined handlers, there may also be others defined by other modules.

As for performance, I’ve often seen Apache Prefork and mod_php regarded as the fastest way to serve PHP. The problem with mod_php is that if Apache is also used to serve static files, memory usage goes through the roof as you have 40 – 100mb Apache processes serving content that could be handled by a much smaller process. Hence I chose to handle the PHP with PHP-FPM, but I did not notice performance loss across 4 different times I’ve rudimentarily tested mod_php vs Apache worker and PHP-FPM. One set up I have seen but I am a bit wary of the setup, and lack the memory most of the time to do it, is using nginx to serve the static content and pass off all the PHP work to Apache with mod_php. I have yet to try out nginx and PHP-FPM.

2 things that made a noticeable difference performance wise to me was setting APC with apc.stat to 0, and moving all the .htaccess configs into the virtualhost and setting AllowOverride None in Apache’s configuration. Setting those means that neither Apache, nor PHP-FPM have to stat most files to serve a page. (There is a caveat to this, as I recall, for APC, something about the way an application includes the file that may still cause PHP to stat the file) If you were not using APC before and saw little performance improvement after using it, then the bottleneck lies elsewhere, for me it’s usually the database if it is not time lost to the actual execution of the PHP application.

I’d be interested to know if you get your performance sorted out.

Hi, thanks for your post. I

Hi, thanks for your post. I followed your instructions but got the 403 error as below. I've also changed permissions on /var/www/fastcgi per the above post. Do you have any idea? Thanks a lot!

You don't have permission to access /php5.fastcgi/phpinfo.php on this server.

Offhand I would say this is a

Offhand I would say this is a problem with file permissions, possibly on the phpinfo.php file, the /var/www directory, excluding /var/www/fastcgi as you said you have already changed those file permissions (which should be www-data:www-data and 0775 I think). Otherwise do you have any Apache access control which may deny access? Such as Order allow,deny on the root directory without specifically allowing access elsewhere?

Excellent tutorial, I think

Excellent tutorial, I think it may be one of the clearest ones on the subject that I’ve found so far.

I am running into a problem though: I’m trying to use your setup for a Drupal site that I have running on an Ubuntu server. The site is in a subdirectory of the web root directory, so when I was using mpm-prefork, I used an htaccess file to redirect requests from root to the Drupal installation and hide the sudirectory so it doesn’t show up in the domain name.
After following your steps, I get a 500 error and it looks like it may have to do with how my rewrite rules interact with fastcgi.

I posted a Drupal Answers question about it here. Do you think you could take a look? I’m sure it’s simple, but I’m not familiar enough with what needs to be directed where to get it to work yet.

I’m pretty sure that using

I’m pretty sure that using fastcgi and Apache worker makes no difference to rewrites. I have found using the Apache RewriteLog and RewriteLogLevel directives to be invaluable when I have come up against mod_rewrite problems. I did have a look and I think it may be the 2 separate rewrites to handle the www or no www redirection that maybe the problem, easiest is just to log the rewrite process and work from there.

I had to increase -idle

I had to increase -idle-timeout to 60:

  1. FastCGIExternalServer /var/www/fastcgi/php5.fastcgi -socket /var/run/php-fpm.sock -idle-timeout 60

Otherwise some slow (Drupal) processes would fail.

Drupal can take quite a while

Drupal can take quite a while to do it's thing at times, but I haven't run into that yet myself. Though our InnoDB buffer pools are generally large enough to hold pretty much everything. Along with dropping some reliability and only flushing the transaction log once a second.

I did this install using the

I did this install using the PHP 5.4 FPM packages from this PPA: https://launchpad.net/~ondrej/+archive/php5
The only difference in the apache config is that the fastcgi socket is at /var/run/php5-fpm.sock rather than the location in your config.
Another thing that took me ages to figure out is that any rewrite rules that map URLs to PHP scripts need adjusting, for example:

RewriteRule ^/?home /home.php [L]

Needs that flag switching to [PT] because otherwise it will get mapped to /php5.fastcgi/home.php. Using passthrough stops it being mapped as a path and lets it remain as a URL.

Do you know if PHP 5.4

Do you know if PHP 5.4 defaults to using a socket connection? When I install from the Ubuntu repositories PHP 5.3 FPM defaults to using a local IP address rather than a socket. Once I ran into the mapping problem you speak about, but it was something I had done incorrectly somewhere along the line, I can't remember how I fixed it. I haven't had to change my rewrites at all, Drupal, PHPMyAdmin, Vimbadmin, WordPress, and some others, all run fine for me without having to touch rewrites and my own rewrites are no different to what they were with mod_php or any other PHP setup, this includes rewrites pointing at PHP files.

Not running with 2 Virtual

Not running with 2 Virtual Host (2 different user/groups)
"FastCGI: server "/var/htdocs/host2/fastcgi/php5.fastcgi" stderr: Primary script unknown"

Apache run under www-data.www-data.
host1 with www-data.www-data works fine.
host2 with u4711.g4711 not.

I give all dirs, files, sockets 777 permission.
Create all dirs.
Change Alias, Action, FastCGIExternalServer names to be unique (with creating dirs), in many combinations, nothing.

This is the expected

This is the expected behaviour. If you wanted to run as another user you would need to create a PHP-FPM process pool for each different user you wanted to use. The FastCGIExternalServer socket definition would have to point to the same socket as the additional PHP-FPM process pool, and have the correct permissions for this to work. You can configure the socket permissions to be wholly available to any user, but this would be ill advised, and may still not function correctly (I have not tried it). You can create a new PHP-FPM pool by copying the existing www.conf file in /etc/php5/fpm/pool.d/ and renaming it, as well as changing the very first configuration directive from [www] to [your-own-pool-name]. You would need to adjust the user, socket location etc in both the Apache configuration and the PHP-FPM process pool configuration to match up to get this to work.

I did everything according to

I did everything according to this tutorial in order to enable the fpm/fastcgi but for unknown reason <?phpinfo();?> states "Server API Apache 2.0 Handler". How can I dig more into this situation to find out where the problem is? I'm using Ubuntu LTS 12.04. Thank you.

Hi, I know this is old post

Hi, I know this is old post but very good. I am using ubuntu 12.04 and getting Permission denied: access to /php5.fastcgi/status/.
I am trying to access the php-fpm status page to see how it performs on local host.
http://localhost/status
Any idea?

Hi Ryan,

Hi Ryan,
I'm kind of going crazy trying to figure out what I'm doing wrong. I've followed your tutorial (really one of the few regarding PHP-FPM with Apache), adjusted the socket location to match the default pool conf, and for some reason, my php scripts simply don't execute, they are just served as plaintext.

Any ideas? I'm at a complete loss, can't find anything online

Hi, I got my php-fpm running

Hi, I got my php-fpm running with apache2 thanks to your page. However there are minor issue I am facing and wondered if you/someone could help me out.
1. I am assuming that /var/www/fastcgi and /var/www/fastcgi/php5.fastcgi are real directories. I made them with mkdir.
2. One I have it running I notice that if I have a file (the usual info.php) inside a folder (like http://somehost/x/info.php) then the directory x had to be both in php5.fastcgi as well as /var/www, otherwise it would not work. So I ended up with php5.fastcgi as a symink to /var/www to overcome that problem.
3. Now it is largely working (wrt html/php) but no jpg/png files are showing. My apache error.log says that (for e.g.) [client 192.168.1.11] FastCGI: server "/var/www/fastcgi/php5.fastcgi/gallery3/var/thumbs/Misc/.album.jpg" stderr: Access to the script '/var/www/fastcgi/php5.fastcgi/gallery3/var/thumbs/Misc/.album.jpg' has been denied (see security.limit_extensions)
I read the part about security.limit_extensions wrt php-fpm, but my question is why are files with jpg/png extensions being handed to php-fpm? Shouldn't those file extensions be handled by apache itself? Do we need some other regex/filter to be added inside the section?

Thanks