Biz & IT —

Web served, part 3: Bolting on PHP with PHP-FPM

After setting up server and security, we get to deliver some active content.

Web served, part 3: Bolting on PHP with PHP-FPM
Aurich Lawson / Thinkstock

A Web server that can only serve out static pages is fine for a lot of folks. If you just want a homepage with a list of your favorite links and some pictures of your cat, then a bare Web server is all you need. However, if you want to learn about doing more interesting stuff—setting up a forum or a wiki, or using popular blogging apps—then you need some way of generating dynamic content—that is, a website that can be changed or updated programmatically, rather than one made simple static files.

As with most Web server-related things, there are many paths to dynamic content. However, some of the most popular Web applications—things like phpBB, MediaWiki, WordPress, and Drupal—use a server-side scripting language called PHP. That's what we're going to install, because it's relatively easy to get PHP up and running and because having PHP available gives you a tremendous amount of flexibility in what you can do with your Web server.

PHP-FPM

One advantage Apache has over Nginx is the ease with which PHP can be enabled. Nginx, unlike Apache, has no ready-made modules to install, so there are several packages we need to pull down and several configuration files to edit to get PHP working. Never fear, though—we'll cover every detail.

We're going to install a particular PHP bundle called PHP-FPM, where the "FPM" part stands for "FastCGI Process Manager." There's an entire article's worth of discussion on the hows and whys of choosing PHP-FPM, but briefly, we're using this particular PHP package because it includes the ability to scale up (or down) the number of PHP processes that are running and handling requests based on load and because it's fast and it integrates well with Nginx.

Installation and extras

We're going to use the version of PHP-FPM available in the main Ubuntu repositories, which as of this article's publication is 5.3.10. Newer versions of PHP are available (the latest stable version is 5.4.9), but not all applications have been fully modified to work with PHP 5.4 yet, so we're sticking with the default for greater compatibility.

Verify that your repository listings are up to date with a quick sudo aptitude update, and then run the following commands to install PHP-FPM and the add-on modules we want:

sudo aptitude install php5-fpm php5-suhosin php5-gd php-apc php5-mcrypt php5-cli php5-curl

(Depending on what's installed, you may be prompted to remove a package named libgd2-noxpm before the installation can continue; if so, go ahead and allow it to be removed. This is a graphics library used by PHP, and it will be replaced with an alternate version during installation.)

Here's what each of those packages does:

  • php5-fpm: The base PHP-FPM package.
  • php5-suhosin: The Suhosin PHP security patch, which helps to make PHP more secure.
  • php5-gd: A PHP module that allows graphics to be drawn directly by PHP scrips. Required for some applications.
  • php-apc: The PHP Alternative Cache, a module that can drastically improve PHP's speed by caching compiled PHP scripts rather than making the server run the same scripts over and over again.
  • php5-mcrypt: A module which gives PHP access to a wide range of encryption and decryption functions. Required for some applications.
  • php5-cli: Gives you the ability to run PHP scripts from the command line. Useful for troubleshooting and because some applications, like MediaWiki, require you to manually execute scripts to do updates or other administrative functions.
  • php5-curl: Gives PHP the ability to use cURL. Required for some applications.

One more thing to install

We want to install one more thing: memcached (that's pronounced "memcache dee," as in the memcache daemon, not "memcached" as in past tense). This application is a key-value store used by many big websites for caching. We want it for a very specific purpose, which we'll get to near the end of our configuration process. For now, just install memcached and its PHP module, and we'll come back to it in a bit:

sudo aptitude install memcached php5-memcache

Where is all this stuff?

Once all this is installed, you'll have a new directory called /etc/php5 with several subdirectories underneath:

The PHP directory. (This is not a photograph of my screen. This is <a href="http://www.secretgeometry.com/apps/cathode/">Cathode</a>.)
Enlarge / The PHP directory. (This is not a photograph of my screen. This is Cathode.)
Lee Hutchinson

All of the PHP-related configuration files we need to edit are in here. There are several, but fortunately we don't need to make too many changes in order to get things set the way they need to be set.

A word on sockets

PHP, Nginx, and memcached need to be able to pass data back and forth to each other. By default, this is done with TCP ports—PHP-FPM listens on one TCP port for Nginx to feed it scripts to execute; memcache listens for data from PHP-FPM on another TCP port. This means that communication between these different processes needs to be run through the network stack—ensuring maximum compatibility, but coming at the expense of speed. It's a lot faster to use Unix sockets for inter-process communication, which dispenses with the networking stack and lets processes talk within the kernel.

Wherever possible in this guide and in any subsequent guide, where there is inter-process communication to be done, we're going to channel it through Unix sockets instead of involving the networking stack. It's easy to set up and while it won't make much of a difference for a small personal site, it's a good thing to learn how to do.

Configuring the modules

If you take a peek inside /etc/php5/conf.d, you'll see several .ini files, each of which are used to tell PHP to load a specific module. For example, the contents of the mcrypt.ini file look like this:

; configuration for php MCrypt module
extension=mcrypt.so

The conf.d directory is parsed by PHP on start-up, and any file inside is included in the main PHP configuration. This is how modules are loaded. If you ever need to disable a specific module, you can comment out its extension line in that module's .ini file, or even delete the .ini file all together.

One change we might want to make is bumping up the amount of memory available to APC, our PHP opcode cache. Its default 32MB of RAM is fine for small sites with a single application, but if you have the free RAM, it's worth bumping up to 128MB. To do this, edit the file /etc/php5/conf.d/apc.ini and add the following line:

apc.shm_size = 128

We need to restart PHP to make the change effective, but we're going to hold off on that for now, since there are more changes to be made.

Configuring the PHP core

PHP-FPM's default configuration, and the default configuration of most of its modules, is mostly good out of the box. The changes we're going to make are almost all focused on switching things over to using Unix sockets instead of TCP ports for inter-process communication, as discussed above.

php.ini

First, open the file /etc/php5/fpm/php.ini for editing. We're going to make three specific changes in this file. Locate the line "post_max_size" and set it as follows:

post_max_size = 2M

The default value of "8MB" means that PHP will accept POST requests of up to 8MB in size; we can reduce this to 2MB for now. This is one of the settings which affects the maximum size of files users are allowed to upload to the Web server (with applications that allow uploads or attachments, like WordPress or phpBB). We'll revisit this setting in a later article, but for now, cranking it down is fine.

Next, find session.save_handler and session.save_path and set them as follows:

session.save_handler = memcache
session.save_path = unix:/tmp/memcached.sock

This reveals why we've installed memcached: we're going to use it to store our PHP sessions. A session is how PHP preserves information about a client over time; a client creates a session when they first connect to your Web application (your forum, or your blog, or whatever), and that session tracks whatever data needs to be tracked so that the Web server continues to recognize the client. Typically, a record of the session is stored on the client's computer using a cookie, and the Web server reads that cookie, learns who the client is, and applies the session data to that client. For a website like Ars Technica, which is based on a modified version of WordPress, PHP sessions ensure that users remain logged in during different visits to the site.

By default, sessions are stored in actual files in the /tmp directory. This can have some potential security implications, but the main drawback is that on a large site, using files for sessions can be slow since a site with many sessions means doing a lot of file system reads and writes. Memcached, though, uses RAM to hold its cache, and we can use memcached's RAM-based key-value store to hold our session data, freeing us from needing to deal with the file system and greatly speeding things up.

There are disadvantages to this approach, though: session data sometimes needs to hang around for a long time, and memcached's cache is RAM-based and isn't persistent across reboots (or even across memcached restarts), so session data stored in memcached is a lot more fragile. Personally, I prefer the speed of keeping as many things in RAM as possible over preserving the longevity of session data; you may have other priorities—if so, you'll want to leave the two session.save keys in their default settings.

www.conf

The big change to make in the www.conf file is switching it from listening on a TCP port to listening on a Unix socket. Open /etc/php5/fpm/pool.d/www.conf for editing and locate the listen setting in the file. It should be set by default to 127.0.0.1:9000, meaning that PHP is listening for incoming scripts on TCP port 9000. Change it as follows:

listen = /var/run/php5-fpm.soc

Additionally, a bit below that are two more lines we need to un-comment by removing the semicolon in front of them:

listen.owner = www-data
listen.group = www-data

These two settings use the "www-data" user (the Nginx user) and its group as the owner of the Unix socket, which ensures that Nginx and only Nginx will be able to pass scripts to PHP-FPM for execution.

Channel Ars Technica