PHP-FPM 5.3 under Nginx

«« See previous post for getting nginx up and running. This post is about getting PHP running as a FastCGI.

PHP 5.3 with FastCGI Process Manager

Running PHP as a FastCGI (or even a regular CGI) means that it’s decoupled from the web server, and if you’ve historically run mod_php, then this is something you’ll have to get used to. For example, restarting your web server won’t refresh your PHP settings. The CGI listens on a port much like the web server itself does, and that’s how the web server passes requests though to PHP. There’s plenty of information on why this is good news, like here.

The nginx wiki has plenty of information and examples of running PHP as a FastCGI, but I had some bad experiences using these examples. Quite possibly this was due to my tuning it badly, but regardless, discovering PHP-FPM was a major result. Furthermore FPM is now bundled with PHP 5.3.3 – I thoroughly recommend, if you can upgrade to PHP 5.3.x that you do so. If you can’t, and you still want to use PHP-FPM, it’s a lot more effort to install and you may have to roll back your version of PHP 5.2.x – I won’t be covering that here, check the various patches here instead.

There doesn’t seem to be a huge amount of information about configuring PHP-FPM in PHP 5.3.3, so I’ll share my configs here. To start with building php-fpm from the downloaded PHP sources is as simple enabling the option. The rest of the build process is as normal, but you’ll have a php-fpm binary installed at /usr/local/sbin/php-fpm

$ ./configure --enable-fpm

The following is a single nginx server block, which defines a virtual host capable of executing PHP via FastCGI.

server {
  listen       80;
  server_name  *.timwhitlock.info timwhitlock.info;
  root         /home/vhosts/timwhitlock.info/httpdocs;
  access_log   /home/vhosts/timwhitlock.info/log/access.log main;
  error_log    /home/vhosts/timwhitlock.info/log/error.log  warn;
  # Process PHP files with fastcgi
  location ~ .php$ {
    if ( !-f $request_filename ) {
      return 404;
    }
    include /etc/nginx/fastcgi_params;
    fastcgi_pass   127.0.0.1:9000;
    fastcgi_index  index.php;
  }
  location / {
    index  index.php;
  }
}

Notice the inclusion of /etc/nginx/fastcgi_params. This file may be bundled with your nginx installation already. If not, it looks like this:

fastcgi_param  QUERY_STRING       $query_string;
fastcgi_param  REQUEST_METHOD     $request_method;
fastcgi_param  CONTENT_TYPE       $content_type;
fastcgi_param  CONTENT_LENGTH     $content_length;

fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
fastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;
fastcgi_param  REQUEST_URI        $request_uri;
fastcgi_param  DOCUMENT_URI       $document_uri;
fastcgi_param  DOCUMENT_ROOT      $document_root;
fastcgi_param  SERVER_PROTOCOL    $server_protocol;

fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;

fastcgi_param  REMOTE_ADDR        $remote_addr;
fastcgi_param  REMOTE_PORT        $remote_port;
fastcgi_param  SERVER_ADDR        $server_addr;
fastcgi_param  SERVER_PORT        $server_port;
fastcgi_param  SERVER_NAME        $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
#fastcgi_param  REDIRECT_STATUS    200;

I also added the following lines, on the recommendation I found here (in fact this is probably what went wrong with my earlier experiments with FPM under 5.2.x). I recommend adding these lines.

fastcgi_connect_timeout 60;
fastcgi_send_timeout 180;
fastcgi_read_timeout 180;
fastcgi_buffer_size 128k;
fastcgi_buffers 4 256k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
fastcgi_intercept_errors on;

That’s got nginx ready to pass PHP execution onto port 9000, so now we have to get PHP-FPM up and running on that port. PHP-FPM has it’s own configuration, which as of this new release has shifted from an XML format to the familiar ini style. This is a great move, firstly because it looks nicer, and secondly because it also allows us to add additional PHP ini settings.

My main php.ini and php-fpm.conf files are under /usr/local/etc which is the default location. The php-fpm.conf file contains global settings and settings for individual resource pools. Each pool listens on its own port, and can have its own PHP settings.

If you need to support multiple hosts as I do, then you’ll probably want each nginx server to pass PHP requests on to a different pool, specified by its port. To achieve this, I removed all resource pools from the main config and added a wild-card inclusion much like I did with the nginx config. My main php-fpm.conf file looks like this:

[global]

pid = /usr/local/var/run/php-fpm.pid
error_log = /usr/local/var/log/php-fpm.log
log_level = notice
emergency_restart_threshold = 0
emergency_restart_interval = 0
process_control_timeout = 0
daemonize = yes

;  pools defined in virtual hosts
include=/home/vhosts/*/conf/php-fpm.include

Then each host has its own config in php-fpm.include as follows.

[mypool]

listen = 127.0.0.1:9000
listen.backlog = -1
listen.allowed_clients = 127.0.0.1

; Unix user/group of processes
user = nginx
group = nginx

; Choose how the process manager will control the number of child processes.
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 10
pm.max_requests = 100

; Pass environment variables
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

; host-specific php ini settings here
php_admin_value[open_basedir] = /home/vhosts/timwhitlock.info/httpdocs:/tmp

Any directives missing here were just left as default. Note that I set the user to nginx, although I don’t know whether this is a good idea, or not. This is possibly just a throw-back to my mod_php days where PHP would always run as the apache user.

Process manager tuning

There’s not much documentation on the pm.* directives, namely max_children, start_servers, min_spare_servers, max_spare_servers, and max_requests. However, they are analogous to other settings you may be familiar with. The well documented Apache directivesMaxClients, StartServers, MinSpareServers, MaxSpareServers and MaxRequestsPerChild are worth a look to make sense of these options.

Once a process has been used it still occupies memory, even when idle, so you don’t want more of these processes running than your server can handle. I kept my spares low, and my max children under a number I was confident the server would have enough memory to cope with. This is not a particular area of expertise for me, so I suggest doing your own research.

To start up PHP, run the program telling it where your configs are:

# php-fpm -c /usr/local/etc  -y /usr/local/etc/php-fpm.conf

If you need to reload any configurations, you can do a graceful reload will the kill command, as follows taking the process id from disk:

# cat /usr/local/var/run/php-fpm.pid | xargs kill -s USR2

Update

Since writing this I’ve improved my set up a bit

  1. Using this init.d script to start, stop and reload PHP configs
  2. Upgrading to PHP 5.3.6: the processes now show the pool names, which is handy
  3. I’m also using a Unix socket instead of a listening port

9 thoughts on “PHP-FPM 5.3 under Nginx”

  1. why are you writing this if statement in your config – this happens anyway in the server – the server will ALWAYS spit a 404 if a file is not found, so this is totally obsolete AND a serious performance problem, because it is done on every request!
    people should start reading here about nginx:
    http://wiki.nginx.org/Configuration
    and AFTER reading the stuff over there start writing their own tutorials!
    Don´t spread these errors!

  2. I follow the linode’s to install php5.3.3, but it didn’t work.
    Now I just use the php5.2.4 with php-fpm.
    Also I’m new gay to learn liunx, did u have any rpm package to install the lnmp quickly.

Comments are closed.