Setting up a LEMP server on Debian 6

>>Skip the long winded description

Downloading Packages
Vhost Configuration
Testing
Installing MySQL
Installing phpMyAdmin
Setting up SFTP

I’ve been playing with linux as a hobby for about 13 years and I’ve been running my own virtual servers for web and mail for about 2. In all that time, my only web server experience has been with Apache. Whether it was at work, or with my personal sites (shared hosting and VM), or my hobby box at home. I recently decided to try using Nginx instead of Apache. I’ve always wanted to chronicle one of my adventures in server land, and this is my first attempt at doing it.

I currently have my virtual machines with Linode, and I have nothing but good things to say about them. The idea with an unmanaged VM is that they don’t have to help you with your shit; you’re on your own. However, they have been very helpful the couple of times I’ve had a question. They also have an awesome library of helpful docs, and a great forum community. For this adventure, I started out by following one of their LEMP guides in the library: LEMP Server on Debian 6 (Squeeze)

Round 1:

After running through the guide, I had a working LEMP server. I run a few WordPress sites, so the next thing I did was install WordPress. It worked pretty nicely right out of the box, but I did have to fiddle with the site’s vhost config to get anything but the default permalinks to work. I’m not going to post that code here, because I would end up finding better WordPress code later at the Nginx wiki.

At this point I got to thinking about some of the other useful stuff that I used to do with .htaccess, and started looking for ways to accomplish the same goals with Nginx. The first thing that came to mind was setting per-directory/site php settings. A bit of googling turned up an answer; put something like this in your vhost config:

[note color=”#DDD”] [/note]

Unfortunately, I was unable to make this work. I tried and tried and tried, using various sytnax changes and config tweaks, but it was a no go. Perhaps the specific versions and methods I had used just didn’t support what I was trying to do, but I don’t really know. I learned a few things while googling though: I learned that there is more than one way to setup Nginx and PHP. In fact, the preferred method seems to be PHP-FPM, which the Linode guide did not use. Here’s a quote from a config guide that is linked to from the Nginx Wiki:

[note color=”#DDD”][quote style=”1″]If you are not using PHP-FPM, you really should be as opposed to the old spawn-fcgi or php-cgi methods. For debian lenny/squeeze dotdeb makes php5-fpm available[/quote][/note]

That touches on another thing I learned: that you can get more up-to-date LAMP/LEMP components from dotdeb.org by adding their repository to your sources.list. Many of the guides I came across did this, so I figured I’d give it a go in round 2.

Round 2

Downloading Packages

I started again with a fresh install of Debian 6. After basic config (setting timezone, hostname, etc) and security setup, I added the dotdeb.org repositories to my sources.list:

[note color=”#DDD”]nano /etc/apt/sources.list
[/note]

Then I grabbed the GnuPG key, added it to the server, and updated APT:

[note color=”#DDD”]wget http://www.dotdeb.org/dotdeb.gpg
cat dotdeb.gpg | sudo apt-key add –
apt-get update[/note]

Next I installed Nginx and PHP-FPM (and some other php modules):

[note color=”#DDD”]apt-get install nginx
apt-get install php5 php5-fpm php-pear php5-common php5-mcrypt php5-mysql php5-cli php5-gd php5-curl[/note]

Remember the quote above about how I should have been using PHP-FPM? Well, that same author had something to say about using unix sockets vs TCP, and I’ve got a good feeling about that guy, so I decided to go the same route.

[note color=”#DDD”][quote style=”1″]I prefer to use a unix socket, it cuts out the TCP overhead, and increases security since file-based permissions are stronger.[/quote][/note]

Speaking of file-based permissions, another thing I came across while googling was how to set up Nginx for a shared hosting environment. I learned that you can configure seperate “pools” for PHP-FPM that will allow each site to execute PHP scripts as the user/group defined in that pool. This should also work well with using SFTP to grant access to a website directory, which I intend to do.

So far, there are 3 major differences between my new setup and the Linode guide:

  1. PHP-FPM instead of PHP with FastCGI
  2. PHP listening on UNIX sockets instead of TCP ports
  3. Defining PHP-FPM pools for my sites to use.

Vhost Configuration

Ok, next up are configuring a virtual host and pool for my site. I’m going to start with the pool, since the settings I use there will influence my vhost config.

The biggest differences between my new pool and default one (www.conf) is that I’ll be using a new user/group that I’ll create, and I’ll be using unix sockets. For a detailed explanation of each line, have a look at the default pool (/etc/php5/fpm/pool.d/www.conf). Here is my example config:

[note color=”#DDD”] nano /etc/php5/fpm/pool.d/example.com.conf
[/note]
Here’s a handy dandy find and replace method for using something like I posted above as a template. Add a little bash scripting to copy the template and do the find/replace, then do that for a few other config files, and you’ve got a pretty simple method of adding a new site to your server. :)
[note color=”#99CCFF”]sed -i 's/searchfor/replacewith/g' /path/to/file[/note]

Next I created the user referenced in that config:

Note: the password chosen here will be used later for SFTP purposes.

[note color=”#DDD”]useradd -M -s /sbin/nologin example
passwd example[/note]

Where:

  • -M: do not create the user’s home directory
  • -s: login shell of the new account

By setting the login shell to /sbin/nologin, I’m effectively blocking login. This isn’t strictly necessary, since this user will be restricted to SFTP only, but it makes me feel warm and fuzzy inside to have another layer of security.

Next I created my vhost config. Take a look at the default config (/etc/nginx/sites-available/default) if you’d like to see a commented example. Also, right up at the top you’ll find this:

[note color=”#DDD”]# You should look at the following URL’s in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# http://wiki.nginx.org/Pitfalls
# http://wiki.nginx.org/QuickStart
# http://wiki.nginx.org/Configuration
[/note]

I took the comment’s advice and visited those sites, and I’m so glad that I did. If you only check out one of those sites, let it be the Pitfalls. Turns out that the vhost example in the original Linode guide contained a number of those pitfalls. Here are some things I changed thanks to the Pitfalls page:

  1. I moved the root directive from the location block to the server block. (To be fair to the Linode guide, their first example puts it in the location block, but the later example which includes a PHP section has it done correctly.)
  2. I moved the index directive from the location block to the http block (in /etc/nginx/nginx.conf)
  3. I took the hard coded path out of SCRIPT_FILENAME and replaced it with $document_root

Here is my example vhost config:

[note color=”#DDD”]nano /etc/nginx/sites-available/example.com

[/note]

That config is based on one that you can find by following the Configuration link above. It’s under PHP, WordPress and Drupal Configuration Examples. Following the advice on that page, I created two includes: drop.conf and php.conf. The first one, drop.conf, denies access to various things like hidden files, while php.conf contains the basic php config that I’ll use for most sites. You can find commented versions of these files at the aforementioned link. You’ll notice that after I include php.conf I define the unix socket that this vhost should use. I can’t put that line in the conf since it needs to be unique for each vhost.

[note color=”#DDD”] nano /etc/nginx/drop.conf
[/note] [note color=”#DDD”] nano /etc/nginx/php.conf
[/note]

Then I put the index directive in the http block of nginx.conf. I won’t need to put it in a vhost config unless a site has a special need for it.

[note color=”#DDD”]nano /etc/nginx/nginx.conf
[/note]

One more thing. Both the Configuration example I linked to above, and the default vhost config say to set cgi.fix_pathinfo to 0 in php.ini, so I did that.

[note color=”#DDD”]nano /etc/php5/fpm/php.ini

[/note]

And here’s the example’s explanation for why I just did that:

[note color=”#DDD”][quote style=”1″]REMEMBER: Set cgi.fix_pathinfo to 0 in the php.ini when using Nginx with PHP-FPM/FastCGI, otherwise something as simple as /forum/avatars/user2.jpg/index.php could be used to execute an uploaded php script hidden as an image.[/quote][/note]

There is a section in the Pitfalls page that details the issue and lists several ways to address it. I went with “try_files $uri =404;” in the php block of my vhost config. Read the Passing Uncontrolled Requests to PHP section of the article for more information.

Next I created a symbolic link from my vhost config in sites-available to sites-enabled:

[note color=”#DDD”]ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com[/note]

I also removed the symbolic link for the default site:

[note color=”#DDD”]rm /etc/nginx/sites-enabled/default[/note]

Then I created the directories referenced in my vhost config and assigned ownership to the user/group defined in the pool for this site:

[note color=”#DDD”]mkdir -p /var/sites/example.com/public_html
mkdir -p /var/sites/example.com/logs
chown example:example /var/sites/example.com/public_html[/note]

Testing

Finally, I created an index.php file, rebooted nginx and php, then checked to see if everything was working.

[note color=”#DDD”]nano /var/sites/example.com/public_html/index.php
[/note] [note color=”#DDD”]/etc/init.d/nginx restart
/etc/init.d/php5-fpm restart[/note]

Everything worked! I was able to browse to my site and see “OH HAI WORLD”, which made me giggle.

I also created a phpinfo file, so that I could make sure PHP was running as the user specified.

[note color=”#DDD”]nano /var/sites/example.com/public_html/phpinfo.php
[/note]

In the Environment variables I could see that USER reflected the user that I defined in the pool for my site. HOORAY!

One last test was to try and change a PHP ini setting using the method listed at the very beginning of this post. I really held my breath for this one, but it worked!!! Color me happy.   :)

Installing MySQL

Many of my sites are running WordPress, so I still needed to install MySQL and phpMyAdmin, and tweak the vhost config to work better with WordPress. First I installed the MySQL packages. During the installation process you will be prompted to set a password for the MySQL root user.

[note color=”#DDD”]apt-get install mysql-server php5-mysql[/note]

After they installed, I issued the following command to secure MySQL:

[note color=”#DDD”]mysql_secure_installation[/note]

First I had to type in the password that I created in the previous step. I said “no” to changing the root password and “yes” to everything else.

Then I was able to login to MySQL:

[note color=”#DDD”]mysql -u root -p[/note]

I’m not a MySQL guru, so I’ll be using phpMyAdmin for most of my database administration.

Installing phpMyAdmin

First, I installed the package:

[note color=”#DDD”]apt-get install phpmyadmin[/note]

It automatically launched the setup, and here are the settings I chose:

[note color=”#DDD”]Web server to reconfigure automatically: none
Configure database for phpmyadmin with dbconfig-common? No[/note]

The reason I did not select an option on the first question is because the only options were Apache or Lighttpd. As for answering “no” to the second question… I was just following a guide. :) That guide also had the configuration I used for my vhost config, which I only modified slightly.

[note color=”#DDD”]I added this to my site’s vhost config:
[/note]

One caveat: because of the fact that my website is running php as it’s own user, I had to change the group of /var/lib/phpmyadmin/blowfish_secret.inc.php to match the group of my php user. (It was previously set to www-data)

[note color=”#DDD”]chown example /var/lib/phpmyadmin/blowfish_secret.inc.php[/note]

Setting up SFTP

I used this Linode guide as a baseline, but I setup my sshd_config a little differently than their guide. I setup a Match block to restrict the user “example” to SFTP and the web site directory /var/sites/example.com. That directory is owned by root, but has a sub directory of public_html that is owned by example (the user). With this setup, that user can login and see their logs and the public_html directory, but they can only write to public_html.

[note color=”#DDD”]nano /etc/ssh/sshd_config
[/note]

You’ll notice that this user is allowed to authenticate via password. In the main config and I have PasswordAuthentication set to no and I use SSH Keys to access the server, but when granting SFTP access, I want the experience to be as close to traditional FTP as possible.

NEXT: wordpress vhost config, bash script for adding sites

Category(s): Googling for Answers, Linux, Nginx
Tags: , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

* Copy this password:

* Type or paste password here: