After some time with node.js I recently decided to move to nginx + php-fpm + node.js for my future servers. Here we will be installing:

  • Nginx as a fast HTTP server with reverse proxy to node.js
  • php-fpm for running PHP scripts. The php-fpm (PHP FastCGI sapi) is built into the PHP core but only since 5.3.3, so we need a recent version of PHP.
  • node.js for handling comet and high-concurrency/persistent connections.
  • Monit is used to restart node.js in case of errors.

For all the packages other than node.js (obviously, since it's new/actively changing), you can get the most recent version without to patching/compiling anything anymore.

Start by updating your packages and uninstalling httpd if you had it installed:

yum update
yum remove httpd

Add repositories for nginx and PHP-fpm

Just add the EPEL and Remi repos: bash rpm -Uvh http://download.fedora.redhat.com/pub/epel/5/i386/epel-release-5-4.noarch.rpm rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-5.rpm

Install nginx and PHP-fpm

Remi has the php-fpm package (php 5.3.4); EPEL has nginx:

yum --enablerepo=remi install php-fpm nginx

Some other common packages:

yum --enablerepo=remi install mysql mysql-server php-mysql php-common php-gd php-mbstring php-mcrypt php-xml php-gd php-bcmath

PHPunit and PHPdocumentor:

yum --enablerepo=remi install php-channel-phpunit php-pear-PhpDocumentor php-phpunit-PHPUnit

Create a test application

mkdir /var/www/

In /var/www/index.php:

<?php
  phpinfo();

Configure nginx

In /etc/nginx/nginx.conf:

server {
   listen       80;
   servername  ;
   access_log  logs/host.access.log  main;
   root   /var/www;
   index  index.php index.html index.htm;
   location ~ .php$ {
      # Security: must set cgi.fixpathinfo to 0 in php.ini!
      fastcgi_split_path_info ^(.+.php)(/.+)$;
      fastcgi_pass 127.0.0.1:9000;
      fastcgi_index index.php;
      fastcgi_param SCRIPT_FILENAME          $document_root$fastcgi_script_name;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      include /etc/nginx/fastcgi_params;
   }
}

Default fastcgi_params

I start the php location by including /etc/nginx/fastcgi_params. This is because I want to use those as defaults, then override them individually afterwards. It is also recommended to have one root directive per server.

PHP path

I use fastcgi_split_path_info which became available in 0.7.31 to pass PATH_INFO.

Security settings

IMPORTANT: Change cgi.fix_pathinfo to 0 in php.ini to prevent a security issue which arises with the default PHP configuration when PHP incorrectly tries to guess which file you want for URLS specifying nonexistent files. Setting cgi.fix_pathinfo=0 causes PHP to only try the literal path given. Alternatively check that the file exists: if (!-f $request_filename) { return 404; }

See also: Nginx pitfalls.

Start/restart and test

service php-fpm start
service nginx restart

Test by going to your server root, it should show you your phpinfo().

Prepare to install node.js

Excellent post: http://wavded.tumblr.com/post/475957278/hosting-nodejs-apps-on-centos-5 Add a user for node.js: bash groupadd -r node useradd -r --shell /bin/bash --comment 'User for running node.js' -g node --home /var/lib/node node I prefer using /var/lib/node rather than /home/node because this is more in line with how other server daemon users are defined (e.g. nginx, mysql). -r is for --system, --comment is for --gecos.

Install node.js

yum install gcc-c++ openssl-devel
wget --no-check-certificate https://github.com/ry/node/tarball/v0.3.3
tar -xzvf ry-node-v0.3.3-0-g57544ba.tar.gz
cd ry-node-v0.3.3-0-g57544bac1
./configure
make
make install
mkdir /var/node

Create a test application

For node.js apps, I prefer to keep the www and node trees separate. Also note that I am running unstable 0.3.3 not 0.2.x, so the example is slightly different. Create the example file /var/node/hello_world/example.js:

var sys = require("sys"),
   http = require("http");
http.createServer(function (request, response) {
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.end("Hello Worldn");
}).listen(8000);
sys.puts("Server running at 127.0.0.1:8000");

Configure nginx for node.js (subdirectory approach)

In /etc/nginx/nginx.conf (after the server definition has started): bash location /node { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://127.0.0.1:8000/; proxy_redirect off; }

Install monit

yum install monit

Create monit script

/etc/monit.d/hello_world content: bash check host hello_world with address 127.0.0.1 start program = "/usr/local/bin/node /var/node/hello_world/example.js" as uid node and gid node stop program = "/usr/bin/pkill -f 'node /var/node/hello_world/example.js'" if failed port 8000 protocol HTTP request / with timeout 10 seconds then restart

Start/restart and test

service monit start
service nginx restart

Test by going to /node on your server.

Also, run ps -Af to verify that node is running with the uid node.

The reboot test

Finally, test everything by restarting your server. A friendly reminder:

chkconfig nginx on

chkconfig monit on

chkconfig php-fpm on

chkconfig mysqld on

chkconfig --list

Comments

Andy: Great write up, still works on CentOS 6. Thanks!

Chris Abernethy: Agreed, this is a great writeup on a good alternative to the standard apache stack.

If you're interested in also managing node.js with RPM, check out my post on that here: http://www.chrisabernethy.com/installing-node-js-on-centos-redhat/

dim: Hi, thx for this post, i am trying to do something similar on ubuntu, and he question i have is:

Does your setup works with simple websocket server written in nodejs (with socket io for example).

And 2nd question: what version of nginx do you use?

@ref https://github.com/LearnBoost/socket.io/wiki/Nginx-and-Socket.io

thanks !

nico: Hi... recently i read this, mmm... it's really faster than previous configuration?