Ubuntu 10.04 with Nginx, Ruby and PHP

Despite the numerous guides I came across for this kind of thing, all were lacking. They had errors, were confusing or just didn't work. To try and prevent this from being the case here I have tested this tutorial by wiping my server clean and following it all over again, line by line to make sure it works. In fact this very site is run on a Linode server built using this tutorial.

Our journey beings SSH'd into our server ssh you@yourserver.com...

What editor would you like to use?

I hate it as much as you do when you can't copy and paste commands because someone went and prefixed them with vi, nano, joe, emacs and so on. For this reason this guide uses the editor command which fires up your editor of choice. Just run the following command and pick your favourite! If you're confused don't worry. Just skip this command, you'll be using nano, a user friendly editor, by default.

update-alternatives --config editor

Add a New User

Ubuntu and sudo go hand in hand. Often server installs allow you to login as root which is no good thing. So lets go ahead and add a user. Replace chris with your cool username of choice.

adduser chris
visudo

Now we are looking at our sudoers file which defines who can sudo. We'll add the line chris ALL=(ALL) ALL to this file under root ALL=(ALL) ALL.

sudo usermod -a -G www-data chris
exit

After adding our user to www-data group (more on this later) we exit. Now would be a very good time to log back in as your new user e.g ssh yourusername@yourserver.com

Disable Root Login

Let's stop that nasty root user from ever logging in.

sudo editor /etc/ssh/sshd_config

In this file change PermitRootLogin yes to PermitRootLogin no.

sudo /etc/init.d/ssh restart

We've just restarted the ssh server for that change to become effective immediately.

Password-less Login

This is an optional extra but a handy one at that. If you don't have a public key run the first command below, otherwise skip it. I hope you're running on a *nix based system (OS X, Linux, FreeBSD). For Windows users you can ask Google how to do this or just skip this step.

When you run ssh-keygen you can choose to generate a password or not. It's a little more secure if you generate a password as if someone gets their filthy mitts on your private key they'd need a password to use it. However generating such a key will mean you'll either have to type in the key's password every login (hardly passwordless) or use something like SSH Agent to remember the password for you. OS X users get this handled automatically with a nice little password prompt box.

ssh-keygen -t rsa
scp ~/.ssh/id_rsa.pub you@yourserver:~/.ssh/authorized_keys
exit

Make sure you change the you@yourserver part. Then we can copy our public key over the server and all should be good. Finally we again. Now is another good time to log back into your server to test the password less login.

Upgrade your fresh install of Ubuntu 10.04

Let's do a quite update of our repository sources and then a quick system upgrade to make sure we have the latest and greatest.

sudo apt-get update
sudo apt-get -y upgrade

Get some build tools

Since we're about to compile something we'll need to get a few pieces of software such as a compiler, also it's making sure you have wget at this point. wget is a handy way of downloading files from the Internet.

sudo apt-get -y install build-essential wget

Compile and install Ruby 1.9.2

It's Ruby time. Now it's worth stating that if you don't want Ruby you can skip this along with the next two steps with on caveat. You'll have to install nginx yourself but don't worry it's just sudo apt-get install nginx.

You might wonder why we are about to compile ruby instead of a quick sudo apt-get install ruby? Well if you're planning on running Rails the version that Ubunut 10.04 comes with is Ruby 1.8.7 p249 which has marshalling bugs that crash Rails 3.0. Of course you might want to use Rails 2.x or not use rails at all. In which case you can skip this step with a simple sudo apt-get instal rails rails-dev rubygems.

sudo apt-get -y install libcurl4-openssl-dev zlib1g-dev libxml2-dev libxslt-dev libssl-dev libpcre3-dev
cd /usr/local/src
sudo wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p0.tar.gz
sudo tar -xvf ruby-1.9.2-p0.tar.gz
cd ruby-1.9.2-p0
sudo ./configure
sudo make
sudo make install
ruby -v

If you see something like ruby 1.9.2p0 (2010-08-18 revision 29036) [i686-linux] reported it's happy days!

Update RubyGems

Let's just make sure rubygems are up to date.

sudo gem update --system
gem -v

Again you should see something like 1.5.2.

Install Passenger

Everyone loves Passenger aka mod_rails. It makes deploying Ruby apps much nicer so let's take advantage of it. First we'll install it via the wonders of gem then set it up.

sudo gem install passenger
sudo passenger-install-nginx-module

You get to choose between Option 1 or 2. Choose 1 unless you really want to choose 2 and know what you are doing. Passenger will then set off downloading and compiling Nginx for you. Which it does because unlike Apache, Nginx does not have modules that you can plug-in. Any add-ons must be compiled into nginx which is what is happening now when all the Passenger magic gets added.

Setup Nginx

Since nginx was installed to /opt/nginx we can create a few symlinks and folders to make it a bit more like the default Ubuntu package of Nginx. Of course, you can skip this step if you'd prefer.

sudo ln -s /opt/nginx/sbin/nginx /usr/local/sbin/nginx
sudo ln -s /opt/nginx/conf /etc/nginx
sudo mkdir /etc/nginx/sites-available /etc/nginx/sites-enabled
sudo mkdir /var/log/nginx
sudo editor /etc/nginx/nginx.conf

Now we need to edit the Nginx config file. At the top change user nobody; to user www-data; and then at the bottom of this file just before the very last } add include sites-enabled/*;. This will run the server as www-data and include all config files under /etc/nginx/sites-enabled which gives us a handy way of enabling/disabling sites by creating and removing symlinks inside this directory that point to /etc/nginx/sites-available/SITE_NAME.

One more thing to edit in this config file we'll need to add the following:

upstream php {
    server 127.0.0.1:9000;
}

index  index.html index.htm;

You need to place this within the http {} section. Try placing it under the line default_type application/octet-stream;.

We have one more config file that needs editing /etc/nginx/fastcgi_params. Open it up and place fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; at the top. Interestingly enough this line already exists in the file /etc/nginx/fastcgi.conf which is identical save the missing line (is this a mistake?). However, it seems that the default is /etc/nginx/fastcgi_params on Debian based systems such as Ubuntu so we'll use that.

Nearly done with Nginx now but we need a way to control it. Since Ubuntu uses upstart lets try our hand at creating an upstart file.

sudo editor /etc/init/nginx.conf

Now just paste the following in:

description "Nginx HTTP Server"

start on runlevel [2]
stop on runlevel [016]

console owner
exec /usr/local/sbin/nginx -c /etc/nginx/nginx.conf -g "daemon off;"

respawn

Finally let's restart Nginx.

sudo start nginx

User permissions

Lets create a home for our website/webapps and then set it's permissions. Since we added our user chris to www-data we'll be able to write/read files in /var/www that are owned by www-data.

sudo mkdir /var/www
sudo chown www-data:www-data /var/www
sudo chmod 775 /var/www/

Install PHP and PHP-FPM

I think we are about halfway through now so top up on coffee if you wish. Otherwise let's get on with the rest of this.

You might not want PHP, if you don't just skip this step and remove php5-mysql from the apt-get line of "Install MySQL" and don't bother running that php5-fpm restart command!

Brian Mercer has kindly supplied some pre-compiled PHP-FPM goodness for us so lets go ahead and install it.

sudo apt-get -y install python-software-properties
sudo add-apt-repository ppa:brianmercer/php
sudo apt-get update
sudo apt-get -y install php5-cli php5-common php5-fpm php5-cgi

I'm going to go ahead and install Pear, APC, the Suhosin patch, Memcache and the GD libs. If you don't want any of these, don't install them.

sudo apt-get -y install php-pear php-apc php5-suhosin php5-gd
sudo /etc/init.d/php5-fpm start

After we restart our server PHP-FPM should be up and running on port 9000.

Install MySQL

Time for faithful old MySQL, unless of course you don't want it, just skip this step or replace it with something like sudo apt-get install postgresql postgresql-client php5-pgsql pgadmin3.

sudo apt-get -y install mysql-server php5-mysql mysql-client

If you've installed MySQL before PHP you'll need to remove Apache which gets installed automatically.

sudo apt-get -y --purge remove apache2-mpm-prefork apache2-utils apache2.2-bin apache2.2-common

Finally lets restart PHP-FPM so it picks up the MySQL extentions

sudo /etc/init.d/php5-fpm restart

Install SQLite3

Again don't install this unless you want it!

sudo apt-get -y install sqlite3 libsqlite3-dev
sudo gem install sqlite3-ruby

Install FTP

Do you need FTP? You might be better off using SFTP (FTP over SSH) which is already setup by this stage. However, some programs such as WordPress like to make use of an FTP server to ease the installation of plug-ins. So this one is up to you.

sudo apt-get -y install vsftpd
sudo editor /etc/vsftpd.conf

Not much to edit in this file, we just need to uncomment a line by changing #write_enable=YES to write_enable=YES to allow us to create files and directories. Now a quick restart so the changes can take effect.

sudo restart vsftpd

Ensite & Dissite Commands

Apache's a2ensite and a2disite commands allow you to easily enable and disable websites by creating and removing symlinks in the sites-enabled directory. We can mimic this on Nginx.

cd ~
wget https://github.com/perusio/nginx_ensite/raw/master/nginx_ensite --no-check-certificate
wget https://github.com/perusio/nginx_ensite/raw/master/nginx_dissite --no-check-certificate
sudo mv nginx_* /usr/sbin/
sudo chown root:root /usr/sbin/nginx_*
sudo chmod +x /usr/sbin/nginx_*

Install a Firewall

We'll be using UFW (Uncomplicated Firewall) to ease creation of a few iptable rules.

sudo apt-get install ufw
sudo ufw enable
sudo ufw allow ssh
sudo ufw allow http

The above commands will install UFW (iptables will be installed with ufw), enable UFW, allow SSH (port 22) and HTTP access (port 80). That's all!

Should you ever wish to check the status of UFW and see which rules are active:

sudo ufw status

Where next?

You're all set up and ready to go with Ubuntu 10.04, Nginx, PHP, Passenger, Ruby, FTP, MySQL and SQLite. You just need to create a few config files for your sites in your /etc/nginx/sites-available directory and add your sites to /var/www/example.com.

You should probably research security, how to tweak/optimise PHP-FPM and Nginx, perhaps the setting up of a mail server? There are plenty of good guides out there however I might cover some of these topics in the future.

Further Reading

  • Phusion Passenger users guide for Nginx
  • Nginx Wiki
  • Ubuntu 10.04 Server Guide - Security
  • Ubuntu 10.04 Server Guide - Email Services