Switching your Magento development environment from Apache to nginx

While at the Magento Imag­ine con­fer­ence a cou­ple weeks ago I learned that the entire Magento site and its demo sites all run on nginx instead of Apache. I was aware of that sev­eral alter­na­tives to Apache existed and served var­i­ous niches espe­cially when it comes to deliv­er­ing sta­tic HTML files at speed, how­ever never really inves­ti­gated them prop­erly. Zeus and lighttpd were always just a blur on the horizon.

See­ing that I was con­sum­ing lots of infor­ma­tion about node.js in the last cou­ple weeks, I unavoid­able came across infa­mous claim that node.js was faster than nginx by Ryan Dahl the author of node.js. See­ing that I was com­pletely out of the loop as to how impor­tant this is, I inves­ti­gated nginx a lit­tle more and real­ized that it was indeed a for­mi­da­ble server after PHP-FPM and APC were installed. I took a cou­ple weeks, run­ning tests off and on try­ing to see how much of an advan­tage there was to using nginx, PHP_FPM and APC. Given that now I have an awe­some fast dev rig, the speed for the client showed lit­tle change in my tests even run­ning some­thing like Magento. But I found a dif­fer­ence when I started putting the servers on vir­tual machines via Vir­tual Box with lim­ited specs (as close as pos­si­ble to the t1.mirco AMI Ama­zon has so kindly offered me for one year) Want­ing to play with new tech­nolo­gies, I stopped after three tests and decided that I was on board with nginx, espe­cially since it was adopted by Word­Press and Magento.

I intend to do some more rig­or­ous test­ing and secu­rity analy­sis before launch­ing a pro­duc­tion site on this combo because mak­ing the move from Apache is a huge step. But first steps were get­ting my local ver­sions of Magento Com­mu­nity Edi­tion 1.4.2.0 and 1.5.0.1 run­ning on nginx. Yes I know I men­tioned ear­lier that nginx made lit­tle dif­fer­ence when run­ning on a brand new dev rig so why am I doing this? I firmly believe in build­ing your soft­ware against as close as pos­si­ble tech­nol­ogy stack as your final deploy­ment. When I worked as the Solu­tion Archi­tect on the Para­mount Pictures/Seagate online store, all the off­shore devel­op­ers were build­ing Magento stores on Win­dows. We had night­mares when it came to weekly deploys yet I couldn’t change the pol­icy of an orga­ni­za­tion half way around the world. So just to be safe, if I am going to deploy onto nginx, I am going to code on an nginx server.

I usu­ally have mul­ti­ple vir­tual hosts run­ning on my dev rig to man­age devel­op­ment of exten­sions against mul­ti­ple ver­sions of Magento. There is also a cus­tom build and deploy­ment sys­tem in the back there hold­ing every­thing together with git, but that is another post. The instruc­tions below are only really intended to move a Ubuntu 10.10 devel­op­ment machine to nginx away from Apache. I would advice against blindly copy-pasting what you see here into your pro­duc­tion machine. It will prob­a­bly work but I am not respon­si­ble if any­thing breaks.

First step is to shut­down Apache

sudo ser­vice apache2 stop

I usu­ally try to remove all the unused pack­ages from my sys­tem if I am not going to use them. That is why my build/deploy sys­tem is built on shell scripts as opposed to any­thing fancy like Capis­trano, Maven etc. So the next step is remov­ing Apache from your sys­tem. I resorted to Synap­tic pack­age man­ager for this because a sim­ple sudo apt-get remove apache2 didn’t remove all the pack­ages. Here is a list of all pack­ages I ended up removing.

sudo apti­tude remove apache2 apache2.2-common apache2.2-bin apache2-utils apache2-mpm-prefork libapache-mod-php5

At this point Synap­tic pack­age man­ager insisted that I install the php5-cgi pack­age so I just went along with it assum­ing that I will be able to remove it later on, but also aware that PHP-FPM works with PHP in a CGI con­text so I left it alone.

That should take care of Apache.  The next step is to install nginx and start it up

sudo apti­tude install nginx
sudo ser­vice nginx start

At this point nginx should have gone to your /var/www folder and care­fully dropped in a folder called nginx-default in there to not dis­turb your exist­ing default folder if there is one. Absent­mind­edly it then for­gets where it put its own doc­root and points is default doc­root to /var/www so when you hit your local­host in a browser, you see the default nginx 404 error page. You can drop in an index.html into /var/www to see what nginx will do.

The next step is install PHP-FPM which is a bunch of bina­ries that attach to PHP and run a ser­vice on your local­host on port 9000. Basi­cally nginx queries this ser­vice for the PHP com­piled data to send to the browser.

sudo apti­tude install php5-fpm
sudo ser­vice php5-fpm start

If you are get­ting “[WARNING] [pool www] pm.start_servers is not set” and you are OCD you can set the value in /etc/php/fpm/pool.d/www.conf on line 73.

APC is a PHP opcode cache that can cache the inter­preted PHP script and even vari­ables. From my test­ing it pro­vided almost neg­li­gi­ble change on a dev machine but it may run on a pub­lic server so again I installed it here. Should there be cache refresh issues we will catch it in devel­op­ment as opposed to launch. Zend has a good starter arti­cle on APC if you want to learn more.

sudo apti­tude install php-apc

Php should be up and run­ning but we haven’t told nginx about it yet. There are con­fig­u­ra­tions for doing so which you can get from this How To Forge tuto­r­ial, but we are not going down that path today. We are going to set up two vir­tual hosts which point to two installs of Magento CE 1.4.2.0 and 1.5.0.1. So I am going to make two new files in /etc/nginx/sites-available/ , one called local1420 and the other called local1501 . Here I switch over to the wis­dom of the Magento Wiki page on nginx with a con­fig­u­ra­tion for the nginx.conf and the indi­vid­ual site configurations.

I have found that the nginx.conf set­ting works fine, but I had to tweak the indi­vid­ual site con­figs to work prop­erly. When I have con­firmed with some­one smarter than me that I am doing it as right as pos­si­ble I will prob­a­bly mod­ify the Magento Wiki to reflect the changes I made. For now I will post my file here so you can compare.

The major changes I made was a result of want­ing to avoid http://www.local1420.com and just stick­ing with http://local1420.com so I removed lines 4–10 of the Magento sug­gested con­fig­u­ra­tions. Beyond that I was not using HTTPS on my local dev machine, so I com­mented out fastcgi_param  HTTPS $fastcgi_https;. Also see­ing that I was not devel­op­ing against a mul­ti­store envi­ron­ment, I com­mented out fastcgi_param  MAGE_RUN_CODE default; and fastcgi_param  MAGE_RUN_TYPE store;. The last change I made was to include /etc/nginx/drop.conf which I will describe at the end of the article.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
server {
lis­ten 80;
server_name local1501.com;
root /var/www/local1501.com;

loca­tion / {
index index.html index.php; ## Allow a sta­tic html file to be shown first
try_files $uri $uri/ @handler; ## If miss­ing pass the URI to Magento’s front han­dler
expires 30d; ## Assume all files are cachable
}

## These loca­tions would be hid­den by .htac­cess nor­mally
loca­tion /app/                { deny all; }
loca­tion /includes/           { deny all; }
loca­tion /lib/                { deny all; }
loca­tion /media/down­load­able/ { deny all; }
loca­tion /pkginfo/            { deny all; }
loca­tion /report/con­fig.xml   { deny all; }
loca­tion /var/                { deny all; }

loca­tion /var/export/ { ## Allow admins only to view export folder
auth_basic           “Restricted”; ## Mes­sage shown in login win­dow
auth_basic_user_file htpasswd; ## See /etc/nginx/htpassword
autoin­dex            on;
}

loca­tion  /. { ## Dis­able .htac­cess and other hid­den files
return 404;
}

loca­tion @handler { ## Magento uses a com­mon front han­dler
rewrite / /index.php;
}

loca­tion ~ .php/ { ## For­ward paths like /js/index.php/x.js to rel­e­vant han­dler
rewrite ^(.*.php)/ $1 last;
}

loca­tion ~ .php$ { ## Exe­cute PHP scripts
if (!-e $request_filename) { rewrite / /index.php last; } ## Catch 404s that try_files miss
expires        off; ## Do not cache dynamic con­tent

fastcgi_pass   127.0.0.1:9000;
#       fastcgi_param  HTTPS $fastcgi_https;
fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
#       fastcgi_param  MAGE_RUN_CODE default; ## Store code is defined in admin­is­tra­tion > Con­fig­u­ra­tion > Man­age  Stores
#       fastcgi_param  MAGE_RUN_TYPE store;
include        fastcgi_params; ## See /etc/nginx/fastcgi_params
}
include drop.conf;
}

The last bit of con­fig­u­ra­tion I added was advised by Karl Bess­ing  Basi­cally it involves cre­at­ing a short file in /etc/nginx/ called drop.conf with the fol­low­ing code and includ­ing it in the server sec­tion of your vir­tual hosts. You can read more about what it does on his site.

1
2
3
loca­tion = /fav­i­con.ico { access_log off; log_not_found off; }
loca­tion = /robots.txt { access_log off; log_not_found off; }
loca­tion ~ /\. { deny  all; access_log off; log_not_found off; }

And with that you should have all your con­fig­u­ra­tion files all ready. Next step is to link in your avail­able sites to your enabled sites. Usu­ally you would use a2ensite for some­thing like this in Apache, but I was unaware if any­thing sim­i­lar existed for nginx, so I just linked them in manually.

sudo ln –s /etc/nginx/sites-available/local1420 /etc/nginx/sites-enabled/local1420
sudo ln –s /etc/nginx/sites-available/local1501 /etc/nginx/sites-enabled/local1501

Restart nginx and php5-fpm and you are done.

sudo ser­vice nginx restart
sudo ser­vice php5-fpm restart

If you are installing Magento from scratch you can check the box dis­played dur­ing setup labelled “Web server Apache rewrites”, because it works just fine. If you have any ques­tions or improve­ments, feel free to leave a comment.