NGINX PHP-FPM and a separated UNIX File Socket

NGINX, PHP-FPM, all running on a separated UNIX File system Socket.

If this statement is making you wonder why you are here, you are:

A. Intrigued
B. Google brought you to the wrong Web Site.

If you happen to be in category B, I apologize press back and try again. If you are in Category A then keep reading I have some fun things to show, and some suggestion on how you can make your NGINX web server possibly better.

While this snippet does not say a lot, this snippet is what we are going for. In this snippet you will see a much simplified process list for my NGINX and PHP-FPM processes, though this is what we are working toward.

root     17084  php-fpm: master process (/etc/php5/fpm/php-fpm.conf) 
kevin    17100  \_ php-fpm: pool rackerua 
Preface

To begin and preface this article, this is the exact stack that I am running and I have set this up on a Rackspace Cloud Server. So I know that it is portable and will work on a multitude of different environments. Secondly I know that it works, I know this because the article you are reading was published on a site using it. Furthermore, I have spent several Days working out the bugs and some of the possible issues that can come up with using this type of setup. and I have attempted to comment the different configuration files that I will be showing so that you can make changes to fit your environment. To that end, I will be showing the entire configuration for the server and some of the other things that I have been able to come up with as I have not found anywhere on the whole of the Internet, and believe me I have looked, on how I set this up.


To being here are the specs that I started with

  • Rackspace Cloud Server.
  • Operating System : Debian 6
  • Flavor : 256 MB Ram

It should be noted, while I am a Debian guy, I also have love for my RHEL Brothers too. This guide is specifically for Debian, though it can be used in a RHEL setup, you simply need to make a few changes to the placement of the files that you are modifying, you will also need to use other repositories for your packages, or compile your service from source.


Once the server is provisioned and you have been able to login to the server, I recommend that you do any and all patching. To do this enter this simple command set:

aptitude update 
aptitude dist-upgrade
reboot

Once you have finished updating this set will allow you to restart the server; this ensures that the updates have been installed and initialized completely.

This is where the fun Begins...

It is now time to get your system ready to install NGINX and PHP-FPM.

You can get the source from NGINX and or add the NGINX repositories to your system. There is a Debian specific Repository that you could use, there are also other Distro specific Repositories that you could use if you system happens to not be a Debian Server. You can find more information on these repositories here : NGINX Repositories

While the for-mentioned REPO is perfectly valid, I choose to use the Debian Backports as my repository for NGINX. I chose this because this allowed for an easy configuration, and allowed me to install a more up-to-date version of NGINX on my system while giving me a more supported application for my environment. To add the Debian Backports to your repositories simply execute this echo command:

echo 'deb http://backports.debian.org/debian-backports squeeze-backports main' >> /etc/apt/sources.list.d/backports.list

after this has been added to your Source Repositories you will then need to add the DotDeb Repositories to your system so that you can install PHP-FPM.

I would like to just take a minute to make a shout out to the people over at DotDeb. You people are what make the Open Source Community a great place to develop. Thank you! You can read more about DotDeb and their repositories here. To install the DotDeb Repositories into your system here is a crazy little command set :

echo 'deb http://packages.dotdeb.org stable all' >> /etc/apt/sources.list.d/DotDeb.list
echo 'deb-src http://packages.dotdeb.org stable all' >> /etc/apt/sources.list.d/DotDeb.list 
wget http://www.dotdeb.org/dotdeb.gpg
cat dotdeb.gpg | sudo apt-key add -; rm dotdeb.gpg

This set adds the DotDeb Repositories to your sources, it then adds the GPG key to your system , and finally it removes the GPG key file from your system; so there are no left overs from the installation. After you have added the repositories to your system, update your sources with this simple command :

aptitude update
Installing NGINX & PHP-FPM

Now this is the most complicated and quite possibly the hardest piece to this setup.

Here is the overwhelimg and complicate command set that you will need to enter in order to get NGINX and PHP-FPM installed on your Debian System.

aptitude -t squeeze-backports install nginx-extras; aptitude install php5 php5-fpm php5-common php5-curl php5-dev php5-gd php5-imagick php5-mcrypt php5-memcache php5-mhash php5-mysql php5-pspell php5-snmp php5-sqlite php5-xmlrpc php5-xsl php-pear libssh2-php php5-cli

At this time I create a system user for NGINX.

adduser --system --no-create-home nginx

This command simply creates a system user with no home directory. We will use this User later in our setup.

WOW! that was tough!

Well now that is out of the way we can proceed to setting up NGINX and PHP-FPM to work on your system.

Setup for NGINX

If you are coming from the world of Apache, NGINX will look like Chinese to you. Though you will see that there are some familiar directives and you will see some things that you may recognize. Though I would implore you to go to the NGINX Wiki and read on how things are done in NGINX. Essentially if it does not work right, it is because you did it wrong. Now that might sound harsh though it is the truth. NGINX is stupid simple, and one of the things that I had the hardest time with; was realizing that there was no need for complicated nonsense in my setup. NGINX allows you to change the outlook on your web application from complication to simplicity. So like Steve Jobs used to always say, "It just Works."

In NGINX everything that you will need to mess with is in the directory /etc/nginx. So for this part of the guide go to the directory /etc/nginx and it is here that we will begin changing the configuration files.

There are several Files that are default that you will have to concern yourself with, and then a few others that I created so that I can use in my deployment of Virtual Hosts.

Default Files nginx.conf fastcgi_params Files that I made to simplify my life security mail.conf Before we go crazy and start deleting files and changing functions I recommend that we make a backup for the core files. Here is how you can do this simply and fast.

tar -czf ~/NGINX_Config.tar.gz nginx.conf fastcgi_params

Here is the nginx.conf that I have setup for my System. This can be modified to fit the needs of your environment though this should be good enough for most Production Systems.

user nginx www-data;
worker_processes 4;
pid /var/run/nginx.pid;

events {
    worker_connections 768;
    # multi_accept on;
}

http {
# Basic Settings
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

# Logging Settings
log_format gzip '$remote_addr - $remote_user [$time_local]  '
                '"$request" $status $bytes_sent '
                '"$http_referer" "$http_user_agent" "$gzip_ratio"';

    access_log /var/log/nginx/access.log gzip buffer=32k;
    error_log /var/log/nginx/error.log notice;

# Gzip Settings
    gzip on;
    gzip_disable "msie6";

    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

# Virtual Host Configs
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;

}

Here is the fastcgi_params

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   REQUEST_URI     $request_uri;
fastcgi_param   DOCUMENT_URI        $document_uri;
fastcgi_param   DOCUMENT_ROOT       $document_root;
fastcgi_param   SERVER_PROTOCOL     $server_protocol;
fastcgi_param   SCRIPT_FILENAME     $document_root$fastcgi_script_name;
fastcgi_param   PATH_INFO       $fastcgi_script_name;

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;

Those were the two basic files that you have to use to setup NGINX. Now here are the custom configs that I added to simplify my life. Now I setup a security file, to fend off some basic annoyances. This is
the security file.

## Only requests to our Host are allowed 
#      if ($host !~ ^($server_name)$ ) {
#         return 444;
#      }

## Only allow these request methods ##
## Do not accept DELETE, SEARCH and other methods ##
     if ($request_method !~ ^(GET|HEAD|POST)$ ) {
         return 444;
     }

## Deny certain Referers ###
     if ( $http_referer ~* (babes|forsale|girl|jewelry|love|nudit|organic|poker|porn|sex|teen) )
     {
         return 404;
         return 403;
     }

Finally I setup a file for the mail directives that are normally in the nginx.conf file. I place these commented out lines in a mail.conffile in the /etc/nginx/conf.d/ folder and I leave the lines all commented out. I do this so that I could use it in the future if I ever choose to, though I am fairly sure that I wont. Essentially this part is optional and not necessary. The file created is mail.conf and it should be placed in the /etc/nginx/conf.d/ directory.

#mail {
#       # See sample authentication script at:
#       # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#       # auth_http localhost/auth.php;
#       # pop3_capabilities "TOP" "USER";
#       # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#       server {
#               listen     localhost:110;
#               protocol   pop3;
#               proxy      on;
#       }
#
#       server {
#               listen     localhost:143;
#               protocol   imap;
#               proxy      on;
#       }
#}

Now that you have the basic NGINX configuration out of the way, it is time to setup some Virtual Hosts. In Debian there are two directories that are in the /etc/nginx/ directory. These are sites-available and sites-enabled. The functions for these directories are fairly straight forward. One is the place you house your Virtual Hosts, and the other is where the active Virtual Hosts live. Essentially if you are familiar with Apache, this has the same functionality. You have a Virtual Host Config file in the available directory and you either copy or Symlink the files from one place to the other. To symlink a Virtual Host Config File from one Directory to another you will enter this command.

ln -s /etc/nginx/sites-available/THE.VIRTUAL.HOST.FILENAME /etc/nginx/sites-enabled/THE.VIRTUAL.HOST.FILENAME

As you build your Virtual Hosts I recommend that you do this from within the directory /etc/nginx/sites-available. So for this portion of the guide go to the /etc/nginx/sites-available directory. Here is the Virtual Host file that I have setup for instances using PHP.

server {
    server_name  www.cloudnull.io;
    rewrite ^(.*) http://DOMAINNAME$1 permanent;
}

server {
        listen 80;
        server_name DOMAINNAME;
                root   /var/www/DOMAINNAME/htdocs;
                index index.php;
        include /etc/nginx/security;

# Logging --
access_log  /var/log/nginx/DOMAINNAME.access.log  gzip  buffer=32k;
error_log  /var/log/nginx/DOMAINNAME.error.log notice;

        # serve static files directly
        location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt)$ {
            access_log        off;
            expires           max;
        }

        location ~ \.php$ {
        try_files $uri =404;
                fastcgi_pass unix:/var/run/php5-fpm/DOMAINNAME.socket;
                fastcgi_index index.php;
                include /etc/nginx/fastcgi_params;
        }
}

If you need and or want a Virtual Host that is not using PHP, simply remove the PHP portion of the previous configuration file. That would look like this.

server {
    server_name  www.DOMAINNAME;
    rewrite ^(.*) http://DOMAINNAME$1 permanent;
}

server {
        listen 80;
        server_name DOMAINNAME;
                root   /var/www/DOMAINNAME/htdocs;
                index index.php;
        include /etc/nginx/security;

# Logging --
access_log  /var/log/nginx/DOMAINNAME.access.log  gzip  buffer=32k;
error_log  /var/log/nginx/DOMAINNAME.error.log notice;

        # serve static files directly
        location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|html|xml|txt)$ {
            access_log        off;
            expires           max;
        }
}

Once you are finished with the NGINX Aspect of this deployment, meaning you have replaced your files with the ones I have shown here, and then created your virtual hosts, you are now ready to configure PHP-FPM.

Setup for PHP-FPM

Here you are going to simply add some POOL information to the PHP-FPM setup that you have already done. Unknowingly when you entered the command to install PHP-FPM, the system setup a default configuration for PHP-FPM. So most of the heavy lifting has been done for you. You may have to do one thing and that is create a directory where you want your Sockets to live while they are alive. I placed them here : /var/run/php5-fpm/ You can use this command to create the directory.

mkdir -p /etc/php5/fpm/sockets/

To complete the setup move to the directory /etc/php5/fpm/pool.d/. It is here that you will setup the different UNIX Sockets that PHP-FPM will function on. Please Note, in order for your system to function properly you will need to create a new POOL for every Virtual Host that you had setup. The pool files should follow this naming convention : YOURDOMAIN.conf

Here is a template for the pool files.

[DOMAINNAME]

listen = /var/run/php5-fpm/DOMAINNAME.socket
listen.backlog = -1

; Unix user/group of processes
user = (THE USERNAME OF THE USER THAT OWNS THE SITE FILES)
group = www-data

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

; 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] = /var/www/DOMAINNAME/htdocs:/tmp
The End

Now that you have gone through the whole guide and you have installed and created all of the files needed for your installation, you will need to restart NGINX and PHP-FPM. After you restart both services it will be up and working. You will have NGINX running Fast-CGI with PHP-FPM. Your Installation will be running on all separate UNIX Sockets with separate owners per socket ensuring a more secure environment. Furthermore, your NGINX Setup will be fast and furious. In my own Speed tests, my site running on NGINX vs My Site Running on Apache, This site was more that 3x faster in both response and action. All while on a small 256MB Virtual Server, which in my opinion is ridiculously awesome! Here is a quick Command to restart Both NGINX and PHP-FPM:

service nginx restart; service php5-fpm restart

If there was no Error that popped up you can now go to your domain that you have setup and you will be running good old NGINX and PHP-FPM.


Another Note...

Just to reiterate, this works and needs nothing else, you can change things to better suit your environment though there is almost nothing else that you need to get the services up and running. Secondly, I have not conveniently left something out of this document. At more than 2500 words, I think I gave too much insight, though I hope this helps. I would also like to say Thank you to my fellow Rackers, in particular Patrick, without our bickering about if this could be done, I would have never thought about doing this nor would I have ever done it, so Thank you!