Back to top

Build Nginx + PHP-FPM + APC + Memcache + Drupal 7 on a bare-bone Ubuntu 10.04 or Debian 5 server

I am not hosting administration expert, nor intent to make this tutorial post as generic as it would work for everybody. Since the stack: Nginx + PHP-FPM + APC + Memcache + Drupal 7 is quite on the cutting edge at the moment (Oct.2010), and there is not many 'master' tutorials available. Therefore, I am writing down my notes here for sharing.

This note is also reviewed and improved by Eric E Moore from Brandorr Inc

I got a bare-bone Debian 5 from linode.com, then, here is how it goes:

1.) Configure SSH and accounts to make it more secure; login as root (This step is optional). I used this source http://endofweb.co.uk/2010/10/ubuntu-vps-nginx-mysql-php-fpm-phpmyadmin-...

aptitude update
aptitude upgrade
aptitude install locales
dpkg-reconfigure locales (# then select en_US.UTF8)
aptitude install build-essential
adduser admin
visudo

The visudo command will open up the /etc/sudoers file. All you need to do to grant sudo privileges to admin is to add a line beneath root’s, so that it looks like this:

# User privilege specification
root ALL=(ALL) ALL
admin ALL=(ALL) ALL

Now exit your SSH connection as root, and re-login as admin

Turn off root login

sudo nano /etc/ssh/sshd_config
# Authentication:
PermitRootLogin no

Now go ahead and restart ssh.

sudo /etc/init.d/ssh restart

we’ll go ahead and secure the firewall in your iptables by adding rules:

sudo nano /etc/iptables.rules

Add the rules seen in the example below so that your /etc/iptables.rules looks just like it. These rules state that outgoing connections are just fine, but all incoming traffic to any port other than 80, 443, 22 or 21 is blocked — unless it’s already been established legitimately.

*filter

# Allow loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use the lo0 interface
-A INPUT -i lo -j ACCEPT
-A INPUT -i ! lo -d 127.0.0.0/8 -j REJECT

# Accept established inbound connections
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow all outbound traffic
-A OUTPUT -j ACCEPT

# Allow HTTP and HTTPS connections
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT

# Allow SSH/SFTP
# Change the value 22 if you are using a non-standard port
-A INPUT -p tcp -m state --state NEW --dport 22 -j ACCEPT

# Allow FTP
# Purely optional, but required for WordPress to install its own plugins or update itself.
-A INPUT -p tcp -m state --state NEW --dport 21 -j ACCEPT

# Allow PING
# Again, optional. Some disallow this altogether.
-A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT

# Reject ALL other inbound
-A INPUT -j REJECT
-A FORWARD -j REJECT

COMMIT
Now, load the rules:

sudo iptables-restore < /etc/iptables.rules
sudo iptables-save < /etc/iptables.rules

Last step make sure they’re saved and reloaded at bootup:
For Ubuntu 10.04

sudo nano /etc/network/interfaces.template

For Debian 5

sudo nano /etc/network/interfaces

Insert “pre-up iptables-restore < /etc/iptables.rules" as the third line in the file so it looks like this:

auto lo
iface lo inet loopback
pre-up iptables-restore < /etc/iptables.rules
address 127.0.0.1
netmask 255.0.0.0
broadcast 127.255.255.255
up ip route replace 127.0.0.0/8 dev lo

Now reboot the server.

sudo reboot

2.) Install dependencies:

(There is a great tutorial written in 2008 http://interfacelab.com/nginx-php-fpm-apc-awesome/, based on that, I have updated a few steps, and added a few things for Drupal 7:)

sudo aptitude install make bison flex gcc patch autoconf locate libxml2-dev libbz2-dev libpcre3-dev libssl-dev zlib1g-dev libmcrypt-dev libmhash-dev libmhash2 libcurl4-openssl-dev libpng3-dev libjpeg-dev libxslt-dev libmysqlclient15-dev libfreetype6 libfreetype6-dev

3.) Compile PHP from source with PHP-FPM patches:
Because Drupal 7 still has compatibility issue with PHP.5.3.x at the moment. So, we will use the latest 5.2.x version. At the time that this blog was written, the latest version was 5.2.17

cd /usr/local/src/
sudo wget http://us.php.net/distributions/php-5.2.17.tar.gz
sudo tar -xvzf php-5.2.17.tar.gz
sudo wget http://php-fpm.org/downloads/php-5.2.17-fpm-0.5.14.diff.gz
sudo gzip -cd php-5.2.17-fpm-0.5.14.diff.gz | sudo patch -d php-5.2.17 -p1
cd php-5.2.17
sudo ./configure --enable-fastcgi --enable-fpm --with-mcrypt --with-zlib --enable-mbstring --enable-pdo --with-curl --disable-debug --with-pic --disable-rpath --enable-inline-optimization --with-bz2 --enable-xml --with-zlib --enable-sockets --enable-sysvsem --enable-sysvshm --enable-pcntl --enable-mbregex --with-mhash --with-xsl --enable-zip --with-pcre-regex --with-gd --without-pdo-sqlite --with-pdo-mysql --without-sqlite --with-jpeg-dir=/usr/lib --with-png-dir=/usr/lib --with-freetype-dir=/usr/lib --with-mysql --with-mysqli
sudo make all install
sudo strip /usr/local/bin/php-cgi
sudo cp sapi/cgi/fpm/php-fpm /etc/init.d/
sudo chmod +x /etc/init.d/php-fpm
4.) Install Memcache and APC
Please note when you build the APC extension, make sure you turn off the option to compile for apache, since we will be using Nginx.

sudo pecl install memcache
sudo pecl install apc

5.) Copy & Paste configuration files

sudo cp /usr/local/src/php-5.2.17/php.ini-recommended /usr/local/lib/php.ini
sudo mkdir /etc/php/
sudo ln -s /usr/local/lib/php.ini /etc/php/php.ini
sudo ln -s /usr/local/etc/php-fpm.conf /etc/php/php-fpm.conf

6.) Update Permission for PHP-FPM at /etc/php/php-fpm.conf

<value name="owner">www-data</value>
<value name="group">www-data</value>
<value name="user">www-data</value>
<value name="group">www-data</value>

7.) Compile Nginx from source:
Altough Nginx is already available in the Ubuntu third-party module repositories, the version is updated. Therefore, we are going to complie nginx from the latest stable version. Please go to http://nginx.org/en/download.html to find the lastest stable version:

cd ..
sudo wget http://nginx.org/download/nginx-1.0.5.tar.gz
sudo tar -zxvf nginx-1.0.5.tar.gz
cd nginx-1.0.5.tar.gz
sudo ./configure --sbin-path=/usr/local/sbin --with-http_ssl_module --without-mail_pop3_module --without-mail_imap_module --without-mail_smtp_module --with-http_stub_status_module
sudo make && sudo make install

8.) Edit Nginx configuration at /usr/local/nginx/conf/nginx.conf, please change the lines blew:

user www-data;
worker_processes 6;
events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 10 10;

gzip on;
gzip_comp_level 1;
gzip_proxied any;
gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;

log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx_access.log main;
error_log /var/log/nginx_error.log debug;
include /usr/local/nginx/sites-enabled/*;
}
9.) Concatenate the parameters below to /usr/local/nginx/conf/fastcgi_params

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;

10.) create a SystemV style init script and store it in /etc/init.d/nginx. You can download the script from http://articles.slicehost.com/assets/2007/10/17/nginx , and make it executable sudo chmod +x /etc/init.d/nginx

11.) Set Up Your Site, here I use insready.com as an example:
First, we’re going to prep the directory where your sites will reside by adding them to the www-data group (the server’s). We’ll add your main user to the same group first, to make things run more smoothly.

sudo mkdir -p /srv/www/insready.com/{public_html,logs}
sudo usermod -a -G www-data admin
sudo chown -R www-data:www-data /srv/www
sudo chmod -R 775 /srv/www
sudo mkdir -p /usr/local/nginx/{sites-available,sites-enabled}
sudo nano /usr/local/nginx/sites-available/insready.com

paste
server {
server_name insready.com;
root /srv/www/insready.com/public_html; ## <-- Your only path $
access_log /srv/www/insready.com/logs/access.log;
error_log /srv/www/insready.com/logs/error.log;

location = /favicon.ico {
log_not_found off;
access_log off;
}

location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}

# This matters if you use drush
location = /backup {
deny all;
}

# Very rarely should these ever be accessed outside of your lan
location ~* \.(txt|log)$ {
allow 192.168.0.0/16;
deny all;
}

location ~ \..*/.*\.php$ {
return 403;
}

location / {
# This is cool because no php is touched for static content
try_files $uri @rewrite;
}

location @rewrite {
# Some modules enforce no slash (/) at the end of the URL
# Else this rewrite block wouldn't be needed (GlobalRedirect)
rewrite ^/(.*)$ /index.php?q=$1;
}

location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
#NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}

# Fighting with ImageCache? This little gem is amazing.
location ~ ^/sites/.*/files/styles/ {
try_files $uri @rewrite;
}

location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires max;
log_not_found off;
}
}

12.) Install MySQL

sudo aptitude install mysql-server
sudo mysql_secure_installation

13.) Install Drupal 7

cd /srv/www/insready.com
sudo wget http://ftp.drupal.org/files/projects/drupal-7.0.tar.gz
sudo tar -xvzf drupal-7.0.tar.gz
sudo cp drupal-7.0/* public_html/ -R
sudo chown www-data:www-data public_html -R

13.1. (Optional) Now create a database table, and install Drupal 7. It might be helpful to install phpmyadmin. The detail is no longer the main focus of this blog.

sudo aptitude install phpmyadmin

Hit ESC when the installation prompts you for auto-configuration, because there is no option for Nginx.

sudo ln -s /usr/share/phpmyadmin/ /srv/www/insready.com/public_html/phpmyadmin

14.) Update php.ini file to include three extensions:

sudo ln -s /usr/local/lib/php/extensions/no-debug-non-zts-20060613/apc.so /usr/lib/php5/20060613+lfs/apc.so
sudo ln -s /usr/local/lib/php/extensions/no-debug-non-zts-20060613/memcache.so /usr/lib/php5/20060613+lfs/memcache.so
sudo chmod +x /etc/init.d/php-fpm
sudo nano /etc/php/php.ini

Make the changes below:

extension_dir = "/usr/lib/php5/20060613+lfs/"
extension=apc.so
extension=memcache.so
extension=mysqli.so

15.) Start It Up

cd /etc/rc2.d
* sudo rm S91apache2 if apache2 is installed by phpmyadmin
* sudo /etc/init.d/apache2 stop
sudo ln -s ../init.d/nginx S30nginx
sudo ln -s ../init.d/php-fpm S20php-fpm
sudo ln -s /usr/local/nginx/sites-available/insready.com /usr/local/nginx/sites-enabled/insready.com
sudo php-fpm start
sudo /etc/init.d/nginx start

评论

Thank you !!

Comment: 

Thank you !!
Runs fantastic... !! Drupal 7 runs extremely fast on my cheap Hetzner root-server.
Keep up the good work.

This line

Comment: 

This line

sudo iptables-restore < /etc/iptables.rules

generates a warning about deprecated syntax. Modifying this line...

-A INPUT -i ! lo -d 127.0.0.0/8 -j REJECT

to this...
(just moving the ! before the -i)

-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

appears to do the trick.

A few small hiccups..

Comment: 

A few small hiccups..

Following these instructions, the mysqli.so library was not built, and the directory referenced does not exist.. step 14 does not work as described. (on ubuntu 10.04)

Here's what I did for step 14:

sudo mkdir /usr/lib/php5
sudo ln -s /usr/local/lib/php/extensions/no-debug-non-zts-20060613/apc.so /usr/lib/php5/apc.so
sudo ln -s /usr/local/lib/php/extensions/no-debug-non-zts-20060613/memcache.so /usr/lib/php5/memcache.so
sudo chmod +x /etc/init.d/php-fpm
sudo nano /etc/php/php.ini

make these changes to php.ini:
extension_dir = "/usr/lib/php5/"
extension=apc.so
extension=memcache.so

I don't recall any further deviations fcrm the instructions being necessary -- I skipped the phpadmin piece.. drupal 7 is screaming fast with this set up on my 512MB linode. Thanks for the guide!

Actually, even this leaves a loose end or two...

Comment: 

Actually, even this leaves a loose end or two... the mysql.so php extension is not available if these directions are followed as is, and it cannot be built when building php from source either. Apparently the version of mysql in the repository for ubuntu 10.04 is not compatible with php 5.2.x as far as building that extension is concerned anyway.. I'm guessing the solution to this is to build mysql from source as well.

Interestingly enough, drupal 7 DOES work without that extension enabled, but phpmyadmin will not, nor will drupal 6. Apparently something in the new drupal 7 db class is smart enough to leverage PDO in ways these others cannot.

To enable mysql for Drupal 6

Comment: 

To enable mysql for Drupal 6 you need to make sure that libmysqlclient-dev is installed and add --with-mysql to your php configure options line.

Its already built during php compilation

Comment: 

Hi,

You can safely remove the call in php.ini to load mysqli.so as that was built into php directly, if you look at the ./configure for php in the post.

@Author, I think mysqli.so load directive should be removed from php.ini.

"8.) Edit Nginx configuration

Comment: 

"8.) Edit Nginx configuration at /etc/nginx/nginx.conf, please change the lines blew:"

in your case, the path will be: /usr/local/nginx/conf/nginx.conf

Starting php_fpm Mar 24

Comment: 

"Starting php_fpm Mar 24 15:11:16.405276 [ERROR] fpm_unix_conf_wp(), line 124: please specify user and group other than root "

I think I screw it up in step 6. Can you explain a bit more what do you mean with "Update Permission for PHP-FPM" and where exactly I should put www-data ? Noob - I know!

It's hidden in the markup

Comment: 

There should be xml tags there that aren't showing up in the post...

   <value name="owner">www-data</value>
   <value name="group">www-data</value>


   Unix user of processes
   <value name="user">www-data</value>

   Unix group of processes
   <value name="group">www-data</value>

Why the firewall rules?

Comment: 

What security do you gain by having such restrictive firewall rules? In my opinion, and the opinion of others, this type of configuration isn't needed. It does not improve security at all.

Terminal not my strong suit

Comment: 

I am having trouble logging back in after the viseudo portion after I terminate my root ssh. I carry out the command for ssh admin@###### and it asks for a password. I tried using my root password, but it won't accept it. Where/how do I change this? is it in the etc/sudoers/ files?

thnkas

Comment: 

Drupal 6 is one of the weakest version of the drupal modules which does not enable the msql to in the libmysqlclinet. I think it works fine in the drupal 7 as when I have installed it in drupal 7 there was no errors.

添加新评论